package {
    //test change//

    import flash.display.MovieClip;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    import org.concord.sparks.JavaScript;
    import org.concord.sparks.util.Display;

    public class Probe extends MovieClip {

        private var circuit:Circuit;
        private var connection:Object = null;
        private var id:String;

        private var _dragging:Boolean = false;
        private var _down:Boolean = false;

        public function Probe() {
            //trace('ENTER Probe#Probe');
            super();
            id = this.name;
            trace('Probe id=' + id);

            this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseUp);
        }

        public function getId() {
            return id;
        }

        public function getConnection():Object {
            return connection;
        }

        public function setConnection(connection:Object):void {
            this.connection = connection;
        }

        public function setCircuit(circuit:Circuit) {
            //trace('ENTER Probe#setCircuit');
            this.circuit = circuit;
        }

        public function getTipPos():Point {
            return Display.localToStage(this.parent, new Point(this.x, this.y));
        }

        public function onMouseDown(event:MouseEvent):void {
            _down = true;
			circuit.putProbeOnTop(this);
        }

        public function onMouseUp(event:MouseEvent):void {
            trace('ENTER Probe#onMouseUp');
            this.stopDrag();
            _down = false;
            if (_dragging) {
                _dragging = false;
                circuit.updateProbeConnection(this);
            }
        }

        public function onMouseMove(event:MouseEvent):void {
            //trace('ENTER Probe#onMouseMove');
            if (! _down) {
                return;
            }
            if (_dragging) {
                circuit.updateResistorEndColors(this);
            }
            else {
                _dragging = true;
                this.startDrag();
                if (connection) {
                    disConnect();
                }
            }
        }

        private function disConnect():void {
            JavaScript.instance().sendEvent('disconnect', 'probe', getId(), connection.getId());
            connection = null;
        }
    }
}

package {

    import flash.display.MovieClip;
    import flash.events.MouseEvent;
    import flash.geom.Point;

    import org.concord.sparks.JavaScript;
    import org.concord.sparks.util.Display;

    public class Probe extends MovieClip {

        private var circuit:Circuit;
        private var connection:Object = null;
        private var id:String;

        private var _dragging:Boolean = false;
        private var _down:Boolean = false;

        public function Probe() {
            //trace('ENTER Probe#Probe');
            super();
            id = this.name;
            trace('Probe id=' + id);

            this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            this.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_OUT, onMouseUp);
        }

        public function getId() {
            return id;
        }

        public function getConnection():Object {
            return connection;
        }

        public function setConnection(connection:Object):void {
            this.connection = connection;
        }

        public function setCircuit(circuit:Circuit) {
            //trace('ENTER Probe#setCircuit');
            this.circuit = circuit;
        }

        public function getTipPos():Point {
            return Display.localToStage(this.parent, new Point(this.x, this.y));
        }

        public function onMouseDown(event:MouseEvent):void {
            _down = true;
			circuit.putProbeOnTop(this);
        }

        public function onMouseUp(event:MouseEvent):void {
            trace('ENTER Probe#onMouseUp');
            this.stopDrag();
            _down = false;
            if (_dragging) {
                _dragging = false;
                circuit.updateProbeConnection(this);
            }
        }

        public function onMouseMove(event:MouseEvent):void {
            //trace('ENTER Probe#onMouseMove');
            if (! _down) {
                return;
            }
            if (_dragging) {
                circuit.updateResistorEndColors(this);
            }
            else {
                _dragging = true;
                this.startDrag();
                if (connection) {
                    disConnect();
                }
            }
        }

        private function disConnect():void {
            JavaScript.instance().sendEvent('disconnect', 'probe', getId(), connection.getId());
            connection = null;
        }
    }
}

test change 2

test change 2


/* 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.demux {

    import flash.utils.ByteArray;
    CONFIG::LOGGING {
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.utils.Log;
    }

    /** Constants and utilities for the H264 video format. **/
    public class Nalu {

        private static var _audNalu : ByteArray;
        // static initializer
        {
            _audNalu = new ByteArray();
            _audNalu.length = 2;
            _audNalu.writeByte(0x09);
            _audNalu.writeByte(0xF0);
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu : ByteArray, position : uint) : Vector.<VideoFrame> {
            var units : Vector.<VideoFrame> = new Vector.<VideoFrame>();
            var unit_start : int;
            var unit_type : int;
            var unit_header : int;
            var aud_found : Boolean = false;
            // Loop through data to find NAL startcodes.
            var window : uint = 0;
            nalu.position = position;
            while (nalu.bytesAvailable > 4) {
                window = nalu.readUnsignedInt();
                // Match four-byte startcodes
                if ((window & 0xFFFFFFFF) == 0x01) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if (unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    unit_header = 4;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    if(unit_type ==9) {
                        aud_found = true;
                    }
                    /* if AUD already found and newly found unit is NDR or IDR,
                        stop parsing here and consider that this IDR/NDR is the last NAL unit
                        breaking the loop here is done for optimization purpose, as NAL parsing is time consuming ...
                     */
                    if (aud_found && (unit_type == 1 || unit_type == 5)) {
                        break;
                    }
                    // Match three-byte startcodes
                } else if ((window & 0xFFFFFF00) == 0x100) {
                    // push previous NAL unit if new start delimiter found, dont push unit with type = 0
                    if (unit_start && unit_type) {
                        units.push(new VideoFrame(unit_header, nalu.position - 4 - unit_start, unit_start, unit_type));
                    }
                    nalu.position--;
                    unit_header = 3;
                    unit_start = nalu.position;
                    unit_type = nalu.readByte() & 0x1F;
                    if(unit_type ==9) {
                        aud_found = true;
                    }
                    /* if AUD already found and newly found unit is NDR or IDR,
                        stop parsing here and consider that this IDR/NDR is the last NAL unit
                        breaking the loop here is done for optimization purpose, as NAL parsing is time consuming ...
                     */
                    if (aud_found && (unit_type == 1 || unit_type == 5)) {
                        break;
                    }
                } else {
                    nalu.position -= 3;
                }
            }
            // Append the last NAL to the array.
            if (unit_start) {
                units.push(new VideoFrame(unit_header, nalu.length - unit_start, unit_start, unit_type));
            }
            // Reset position and return results.
            CONFIG::LOGGING {
                if (HLSSettings.logDebug2) {
                    /** H264 NAL unit names. **/
                    const NAMES : Array = ['Unspecified',// 0
                    'NDR',                          // 1
                    'Partition A',                  // 2
                    'Partition B',                  // 3
                    'Partition C',                  // 4
                    'IDR',                          // 5
                    'SEI',                          // 6
                    'SPS',                          // 7
                    'PPS',                          // 8
                    'AUD',                          // 9
                    'End of Sequence',              // 10
                    'End of Stream',                // 11
                    'Filler Data'// 12
                    ];
                    if (units.length) {
                        var txt : String = "AVC: ";
                        for (var i : int = 0; i < units.length; i++) {
                            txt += NAMES[units[i].type] + ","; //+ ":" + units[i].length
                        }
                        Log.debug2(txt.substr(0,txt.length-1) + " slices");
                    } else {
                        Log.debug2('AVC: no NALU slices found');
                    }
                }
            }
            nalu.position = position;
            return units;
        };

        public static function get AUD():ByteArray {
            return _audNalu;
        }
    }
}

/* 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.demux {

    import flash.utils.ByteArray;
    CONFIG::LOGGING {
    import org.mangui.hls.HLSSettings;
    import org.mangui.hls.utils.Log;
    }

    /** Constants and utilities for the H264 video format. **/
    public class Nalu {

        private static var _audNalu : ByteArray;
        // static initializer
        {
            _audNalu = new ByteArray();
            _audNalu.length = 2;
            _audNalu.writeByte(0x09);
            _audNalu.writeByte(0xF0);
        };

        /** Return an array with NAL delimiter indexes. **/
        public static function getNALU(nalu : ByteArray, position : uint) : Vector.<VideoFrame> {
            var len : uint = nalu.length,i : uint = position;
            var unitHeader : int,lastUnitHeader : int = 0;
            var unitStart : int,lastUnitStart : int = 0;
            var unitType : int,lastUnitType : int = 0;
            var audFound : Boolean = false;
            var value : uint,state : uint = 0;
            var units : Vector.<VideoFrame> = new Vector.<VideoFrame>();
            // Loop through data to find NAL startcodes.
            while (i < len) {
                // finding 3 or 4-byte start codes (00 00 01 OR 00 00 00 01)
                value = nalu[i++];
                switch(state)
                {
                    case 0:
                        if(!value) {
                            state = 1;
                            // unitHeader is NAL header offset
                            unitHeader=i-1;
                        }
                        break;
                    case 1:
                        if(value) {
                            state = 0;
                        } else {
                            state = 2;
                        }
                        break;
                    case 2:
                    case 3:
                        if(value) {
                            if(value === 1) {
                                unitType = nalu[i] & 0x1f;
                                if(unitType == 9) {
                                    audFound = true;
                                }
                                if(lastUnitStart) {
                                    // use Math.min(4,...) as max header size is 4.
                                    // in case there are any leading zeros
                                    // such as 00 00 00 00 00 00 01
                                    //                  ^^
                                    // we need to ignore them as they are part of previous NAL unit
                                    units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), i-state-1-lastUnitStart, lastUnitStart, lastUnitType));
                                }
                                lastUnitStart = i;
                                lastUnitType = unitType;
                                lastUnitHeader = unitHeader;
                                if(audFound == true && (unitType === 1 || unitType === 5)) {
                                  // OPTI !!! if AUD unit already parsed and if IDR/NDR unit, consider it is last NALu
                                  i = len;
                                }
                            }
                            state = 0;
                        } else {
                            state = 3;
                        }
                        break;
                    default:
                        break;
                }
            }
            //push last unit
            if(lastUnitStart) {
                units.push(new VideoFrame(Math.min(4,lastUnitStart-lastUnitHeader), len-lastUnitStart, lastUnitStart, lastUnitType));
            }
            // Reset position and return results.
            CONFIG::LOGGING {
                if (HLSSettings.logDebug2) {
                    /** H264 NAL unit names. **/
                    const NAMES : Array = ['Unspecified',// 0
                    'NDR',                          // 1
                    'Partition A',                  // 2
                    'Partition B',                  // 3
                    'Partition C',                  // 4
                    'IDR',                          // 5
                    'SEI',                          // 6
                    'SPS',                          // 7
                    'PPS',                          // 8
                    'AUD',                          // 9
                    'End of Sequence',              // 10
                    'End of Stream',                // 11
                    'Filler Data'// 12
                    ];
                    if (units.length) {
                        var txt : String = "AVC: ";
                        for (i = 0; i < units.length; i++) {
                            txt += NAMES[units[i].type] + ","; //+ ":" + units[i].length
                        }
                        Log.debug2(txt.substr(0,txt.length-1) + " slices");
                    } else {
                        Log.debug2('AVC: no NALU slices found');
                    }
                }
            }
            nalu.position = position;
            return units;
        };

        public static function get AUD():ByteArray {
            return _audNalu;
        }
    }
}

optimize NAL unit parsing use a state machine and byte read instead of word read and jump backwards in the array in case start code not found related to #162


/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Rhino code, released
 * May 6, 1999.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1997-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Igor Bukanov
 *   Ethan Hugg
 *   Milen Nankov
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

START("9.2.1.2 - XMLList [[Put]]");

// element

var x1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");

TEST(1, "<alpha>one</alpha>" + NL() + "<bravo>two</bravo>",
  x1.toXMLString());

x1[0] = <charlie>three</charlie>;
TEST(2, "<charlie>three</charlie>" + NL() + "<bravo>two</bravo>",
  x1.toXMLString());

x1[0] = <delta>four</delta> + <echo>five</echo>;
TEST(3, "<delta>four</delta>" + NL() + "<echo>five</echo>" + NL() + "<bravo>two</bravo>",
  x1.toXMLString());

var y1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
y1[0] = "five";

TEST(4, "<alpha>five</alpha>" + NL() + "<bravo>two</bravo>",
  y1.toXMLString());

// attribute

var x1 = new XMLList("<alpha attr=\"50\">one</alpha><bravo>two</bravo>");
x1[0].@attr = "fifty";
TEST(5, "<alpha attr=\"fifty\">one</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha attr=\"50\">one</alpha><bravo>two</bravo>");
x1[0].@attr = new XMLList("<att>sixty</att>");
TEST(6, "<alpha attr=\"sixty\">one</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha attr=\"50\">one</alpha><bravo>two</bravo>");
x1[0].@attr = "<att>sixty</att>";
TEST(7, "<alpha attr=\"<att>sixty</att>\">one</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

// text node

var x1 = new XMLList("alpha<bravo>two</bravo>");
x1[0] = "beta";
TEST(8, "<alpha>beta</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
x1[0] = new XML("beta");
TEST(9, "<alpha>beta</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
x1[0] = new XMLList("beta");
TEST(10, "<alpha>beta</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("alpha<bravo>two</bravo>");
x1[0] = new XML("<two>beta</two>");
TEST(11, "<two>beta</two>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

// comment

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = new XMLList("<comment>hello</comment>");
TEST(12, new XMLList("<alpha><comment>hello</comment></alpha>"), x1);

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = new XML("<comment>hello</comment>");
TEST(13, new XMLList("<alpha><comment>hello</comment></alpha>"), x1);

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = "hello";
TEST(14, new XMLList("<alpha><beta>hello</beta></alpha>"), x1);

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = new XML("hello");
TEST(15, new XMLList("<alpha><beta>hello</beta></alpha>"), x1);

// PI

XML.ignoreProcessingInstructions = false;
var x1 = new XML("<alpha><beta><?xm-xsl-param name=\"sponsor\" value=\"dw\"?></beta></alpha>");
x1.beta = new XML("<pi element=\"yes\">instructions</pi>");
TEST(16, new XMLList("<alpha><pi element=\"yes\">instructions</pi></alpha>"), x1);

XML.ignoreProcessingInstructions = false;
var x1 = new XML("<alpha><beta><?xm-xsl-param name=\"sponsor\" value=\"dw\"?></beta></alpha>");
x1.beta = new XMLList("<pi element=\"yes\">instructions</pi>");
TEST(17, new XMLList("<alpha><pi element=\"yes\">instructions</pi></alpha>"), x1);

var x1 = new XML("<alpha><beta><?xm-xsl-param name=\"sponsor\" value=\"dw\"?></beta></alpha>");
x1.beta = "processing instructions";
TEST(18, new XMLList("<alpha><beta>processing instructions</beta></alpha>"), x1);

END();

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Rhino code, released
 * May 6, 1999.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1997-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Igor Bukanov
 *   Ethan Hugg
 *   Milen Nankov
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

START("9.2.1.2 - XMLList [[Put]]");

// element

var x1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");

TEST(1, "<alpha>one</alpha>" + NL() + "<bravo>two</bravo>",
  x1.toXMLString());

x1[0] = <charlie>three</charlie>;
TEST(2, "<charlie>three</charlie>" + NL() + "<bravo>two</bravo>",
  x1.toXMLString());

x1[0] = <delta>four</delta> + <echo>five</echo>;
TEST(3, "<delta>four</delta>" + NL() + "<echo>five</echo>" + NL() + "<bravo>two</bravo>",
  x1.toXMLString());

var y1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
y1[0] = "five";

TEST(4, "<alpha>five</alpha>" + NL() + "<bravo>two</bravo>",
  y1.toXMLString());

// attribute

var x1 = new XMLList("<alpha attr=\"50\">one</alpha><bravo>two</bravo>");
x1[0].@attr = "fifty";
TEST(5, "<alpha attr=\"fifty\">one</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha attr=\"50\">one</alpha><bravo>two</bravo>");
x1[0].@attr = new XMLList("<att>sixty</att>");
TEST(6, "<alpha attr=\"sixty\">one</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha attr=\"50\">one</alpha><bravo>two</bravo>");
x1[0].@attr = "<att>sixty</att>";
TEST(7, "<alpha attr=\"<att>sixty</att>\">one</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

// text node

var x1 = new XMLList("alpha<bravo>two</bravo>");
x1[0] = "beta";
TEST(8, "beta" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
x1[0] = new XML("beta");
TEST(9, "<alpha>beta</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("<alpha>one</alpha><bravo>two</bravo>");
x1[0] = new XMLList("beta");
TEST(10, "<alpha>beta</alpha>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

var x1 = new XMLList("alpha<bravo>two</bravo>");
x1[0] = new XML("<two>beta</two>");
TEST(11, "<two>beta</two>" + NL() + "<bravo>two</bravo>", x1.toXMLString());

// comment

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = new XMLList("<comment>hello</comment>");
TEST(12, new XMLList("<alpha><comment>hello</comment></alpha>"), x1);

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = new XML("<comment>hello</comment>");
TEST(13, new XMLList("<alpha><comment>hello</comment></alpha>"), x1);

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = "hello";
TEST(14, new XMLList("<alpha><beta>hello</beta></alpha>"), x1);

var x1 = new XMLList("<alpha><beta><!-- hello, comment --></beta></alpha>");
x1.beta = new XML("hello");
TEST(15, new XMLList("<alpha><beta>hello</beta></alpha>"), x1);

// PI

XML.ignoreProcessingInstructions = false;
var x1 = new XML("<alpha><beta><?xm-xsl-param name=\"sponsor\" value=\"dw\"?></beta></alpha>");
x1.beta = new XML("<pi element=\"yes\">instructions</pi>");
TEST(16, new XMLList("<alpha><pi element=\"yes\">instructions</pi></alpha>"), x1);

XML.ignoreProcessingInstructions = false;
var x1 = new XML("<alpha><beta><?xm-xsl-param name=\"sponsor\" value=\"dw\"?></beta></alpha>");
x1.beta = new XMLList("<pi element=\"yes\">instructions</pi>");
TEST(17, new XMLList("<alpha><pi element=\"yes\">instructions</pi></alpha>"), x1);

var x1 = new XML("<alpha><beta><?xm-xsl-param name=\"sponsor\" value=\"dw\"?></beta></alpha>");
x1.beta = "processing instructions";
TEST(18, new XMLList("<alpha><beta>processing instructions</beta></alpha>"), x1);

END();

revert e4x change that was not implemented


//
// $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.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shape;
import flash.display.Sprite;

import flash.errors.IOError;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.events.TextEvent;

import flash.geom.Point;

import flash.text.TextField;
import flash.text.TextFieldAutoSize;

import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;

import flash.net.URLRequest;

import com.threerings.util.StringUtil;
import com.threerings.util.ValueEvent;
import com.threerings.util.Util;

/**
 * Dispatched when the size of the media being loaded is known.
 *
 * @eventType com.threerings.flash.MediaContainer.SIZE_KNOWN
 */
[Event(name="mediaSizeKnown", type="com.threerings.util.ValueEvent")]

/**
 * A wrapper class for all media that will be placed on the screen.
 * Subject to change.
 */
public class MediaContainer extends Sprite
{
    /** A ValueEvent we dispatch when our size is known.
     * Value: [ width, height ].
     *
     * @eventType mediaSizeKnown
     */
    public static const SIZE_KNOWN :String = "mediaSizeKnown";

    /** A log instance that can be shared by sprites. */
    protected static const log :Log = Log.getLog(MediaContainer);

    /**
     * Constructor.
     */
    public function MediaContainer (url :String = null)
    {
        if (url != null) {
            setMedia(url);
        }

        mouseEnabled = false;
        mouseChildren = true;
    }

    /**
     * Return true if the content has been initialized.
     * For most content, this is true if the media is non-null, but
     * for anything loaded with a Loader, it is only true after the INIT event is dispatched.
     */
    public function isContentInitialized () :Boolean
    {
        return (_media != null) && (_initialized || !(_media is Loader));
    }

    /**
     * Get the media. If the media was loaded using a URL, this will
     * likely be the Loader object holding the real media.
     */
    public function getMedia () :DisplayObject
    {
        return _media;
    }

    /**
     * Configure the media to display.
     */
    public function setMedia (url :String) :void
    {
        if (Util.equals(_url, url)) {
            return; // no change
        }
        _url = url;

        // shutdown any previous media
        if (_media != null) {
            shutdown(false);
        }

        // set up the new media
        willShowNewMedia();
        if (StringUtil.endsWith(url.toLowerCase(), ".flv")) {
            setupVideo(url);

        } else {
            setupSwfOrImage(url);
        }
    }

    /**
     * Configure our media as an instance of the specified class.
     */
    public function setMediaClass (clazz :Class) :void
    {
        setMediaObject(new clazz() as DisplayObject);
    }

    /**
     * Configure an already-instantiated DisplayObject as our media.
     */
    public function setMediaObject (disp :DisplayObject) :void
    {
        _url = null;
        if (_media != null) {
            shutdown(false);
        }

        willShowNewMedia();
        addChild(disp);
        _media = disp;
        updateContentDimensions(disp.width, disp.height);
    }

    /**
     * A place where subclasses can initialize things prior to showing
     * new media.
     */
    protected function willShowNewMedia () :void
    {
        _initialized = false;
    }

    /**
     * Configure this sprite to show a video.
     */
    protected function setupVideo (url :String) :void
    {
        var vid :VideoDisplayer = new VideoDisplayer();
        vid.addEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
        vid.addEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
        _media = vid;
        addChild(vid);
        vid.setup(url);
    }

    /**
     * Configure this sprite to show an image or flash movie.
     */
    protected function setupSwfOrImage (url :String) :void
    {
        startedLoading();

        // create our loader and set up some event listeners
        var loader :Loader = new Loader();
        _media = loader;
        var info :LoaderInfo = loader.contentLoaderInfo;
        info.addEventListener(Event.COMPLETE, loadingComplete);
        info.addEventListener(Event.INIT, handleInit);
        info.addEventListener(IOErrorEvent.IO_ERROR, loadError);
        info.addEventListener(ProgressEvent.PROGRESS, loadProgress);

        // create a mask to prevent the media from drawing out of bounds
        if (getMaxContentWidth() < int.MAX_VALUE &&
                getMaxContentHeight() < int.MAX_VALUE) {
            configureMask(getMaxContentWidth(), getMaxContentHeight());
        }

        // start it loading, add it as a child
        loader.load(new URLRequest(url), getContext(url));
        addChild(loader);

        try {
            updateContentDimensions(info.width, info.height);
        } catch (err :Error) {
            // an error is thrown trying to access these props before they're
            // ready
        }
    }

    /**
     * Display a 'broken image' to indicate there were troubles with
     * loading the media.
     */
    protected function setupBrokenImage (w :int = -1, h :int = -1) :void
    {
        if (w == -1) {
            w = 100;
        }
        if (h == -1) {
            h = 100;
        }
        setMediaObject(ImageUtil.createErrorImage(w, h));
    }

    /**
     * Get the application domain being used by this media, or null if
     * none or not applicable.
     */
    public function getApplicationDomain () :ApplicationDomain
    {
        return (_media is Loader)
            ? (_media as Loader).contentLoaderInfo.applicationDomain
            : null;
    }

    /**
     * Unload the media we're displaying, clean up any resources.
     *
     * @param completely if true, we're going away and should stop
     * everything. Otherwise, we're just loading up new media.
     */
    public function shutdown (completely :Boolean = true) :void
    {
        try {
            // remove the mask (but only if we added it)
            if (_media != null && _media.mask != null) {
                try {
                    removeChild(_media.mask);
                    _media.mask = null;

                } catch (argErr :ArgumentError) {
                    // If we catch this error, it was thrown in removeChild
                    // and means that we did not add the media's mask,
                    // and so shouldn't remove it either. The action we
                    // take here is NOT setting _media.mask = null.
                    // Then, we continue happily...
                }
            }

            if (_media is Loader) {
                var loader :Loader = (_media as Loader);
                var url :String = loader.contentLoaderInfo.url;

                // remove any listeners
                removeListeners(loader.contentLoaderInfo);

                // dispose of media
                try {
                    loader.close();
                } catch (ioe :IOError) {
                    // ignore
                }
                loader.unload();

                removeChild(loader);
                log.info("Unloaded media [url=" + url + "].");

            } else if (_media is VideoDisplayer) {
                var vid :VideoDisplayer = (_media as VideoDisplayer);
                vid.removeEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
                vid.removeEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
                vid.shutdown();
                removeChild(vid);

            } else if (_media != null) {
                removeChild(_media);
            }
        } catch (ioe :IOError) {
            log.warning("Error shutting down media: " + ioe);
            log.logStackTrace(ioe);
        }

        // clean everything up
        _w = 0;
        _h = 0;
        _media = null;
    }

    /**
     * Get the width of the content, bounded by the maximum.
     */
    public function getContentWidth () :int
    {
        return Math.min(Math.abs(_w * getMediaScaleX()), getMaxContentWidth());
    }

    /**
     * Get the height of the content, bounded by the maximum.
     */
    public function getContentHeight () :int
    {
        return Math.min(Math.abs(_h * getMediaScaleY()), getMaxContentHeight());
    }

    /**
     * Get the maximum allowable width for our content.
     */
    public function getMaxContentWidth () :int
    {
        return int.MAX_VALUE;
    }

    /**
     * Get the maximum allowable height for our content.
     */
    public function getMaxContentHeight () :int
    {
        return int.MAX_VALUE;
    }

    /**
     * Get the X scaling factor to use on the actual media.
     */
    public function getMediaScaleX () :Number
    {
        return 1;
    }

    /**
     * Get the Y scaling factor to use on the actual media.
     */
    public function getMediaScaleY () :Number
    {
        return 1;
    }

    /**
     * Called by MediaWrapper as notification that its size has changed.
     */
    public function containerDimensionsUpdated (newWidth :Number, newHeight :Number) :void
    {
        // do nothing in base MediaContainer
    }

    override public function toString () :String
    {
        return "MediaContainer[url=" + _url + "]";
    }

    /**
     * Return the LoaderContext that should be used to load the media
     * at the specified url.
     */
    protected function getContext (url :String) :LoaderContext
    {
        if (isImage(url)) {
            // load images into our domain so that we can view their pixels
            return new LoaderContext(true,
                new ApplicationDomain(ApplicationDomain.currentDomain),
                SecurityDomain.currentDomain);

        } else {
            // share nothing, trust nothing
            return new LoaderContext(false, new ApplicationDomain(null), null);
        }
    }

    /**
     * Does the specified url represent an image?
     */
    protected function isImage (url :String) :Boolean
    {
        // look at the last 4 characters in the lowercased url
        switch (url.toLowerCase().slice(-4)) {
        case ".png":
        case ".jpg":
        case ".gif":
            return true;

        default:
            return false;
        }
    }

    /**
     * Remove our listeners from the LoaderInfo object.
     */
    protected function removeListeners (info :LoaderInfo) :void
    {
        info.removeEventListener(Event.COMPLETE, loadingComplete);
        info.removeEventListener(Event.INIT, handleInit);
        info.removeEventListener(IOErrorEvent.IO_ERROR, loadError);
        info.removeEventListener(ProgressEvent.PROGRESS, loadProgress);
    }

    /**
     * A callback to receive IO_ERROR events.
     */
    protected function loadError (event :IOErrorEvent) :void
    {
        stoppedLoading();
        setupBrokenImage(-1, -1);
    }

    /**
     * A callback to receive PROGRESS events.
     */
    protected function loadProgress (event :ProgressEvent) :void
    {
        updateLoadingProgress(event.bytesLoaded, event.bytesTotal);
        var info :LoaderInfo = (event.target as LoaderInfo);
        try {
            updateContentDimensions(info.width, info.height);
        } catch (err :Error) {
            // an error is thrown trying to access these props before they're
            // ready
        }
    }

    /**
     * Handles video size known.
     */
    protected function handleVideoSizeKnown (event :ValueEvent) :void
    {
        var args :Array = (event.value as Array);
        updateContentDimensions(int(args[0]), int(args[1]));
    }

    /**
     * Handles an error loading a video.
     */
    protected function handleVideoError (event :ValueEvent) :void
    {
        log.warning("Error loading video [cause=" + event.value + "].");
        stoppedLoading();
        setupBrokenImage(-1, -1);
    }

    /**
     * Handles the INIT event for content loaded with a Loader.
     */
    protected function handleInit (event :Event) :void
    {
        _initialized = true;
    }

    /**
     * Callback function to receive COMPLETE events for swfs or images.
     */
    protected function loadingComplete (event :Event) :void
    {
        var info :LoaderInfo = (event.target as LoaderInfo);
        removeListeners(info);

//        trace("Loading complete: " + info.url +
//            ", childAllowsParent=" + info.childAllowsParent +
//            ", parentAllowsChild=" + info.parentAllowsChild +
//            ", sameDomain=" + info.sameDomain);

        updateContentDimensions(info.width, info.height);
        updateLoadingProgress(1, 1);
        stoppedLoading();
    }

    /**
     * Configure the mask for this object.
     */
    protected function configureMask (ww :int, hh :int) :void
    {
        var mask :Shape;
        if (_media.mask != null) {
            // see if the mask was added by us
            try {
                getChildIndex(_media.mask);

            } catch (argErr :ArgumentError) {
                // oy! We are not the controllers of this mask, it must
                // have been added by someone else. This probably means
                // that the _media is not a Loader, and so we should just
                // leave it alone with its custom mask.
                return;
            }

            // otherwise, it's a mask we previously configured
            mask = (_media.mask as Shape);

        } else {
            mask = new Shape();
            // the mask must be added to the display list (which is wacky)
            addChild(mask);
            _media.mask = mask;
        }

        mask.graphics.clear();
        mask.graphics.beginFill(0xFFFFFF);
        mask.graphics.drawRect(0, 0, ww, hh);
        mask.graphics.endFill();
    }

    /**
     * Called during loading as we figure out how big the content we're
     * loading is.
     */
    protected function updateContentDimensions (ww :int, hh :int) :void
    {
        // update our saved size, and possibly notify our container
        if (_w != ww || _h != hh) {
            _w = ww;
            _h = hh;
            contentDimensionsUpdated();
            // dispatch an event to any listeners
            dispatchEvent(new ValueEvent(SIZE_KNOWN, [ ww, hh ]));
        }
    }

    /**
     * Called when we know the true size of the content.
     */
    protected function contentDimensionsUpdated () :void
    {
        // nada, by default
    }

    /**
     * Update the graphics to indicate how much is loaded.
     */
    protected function updateLoadingProgress (
            soFar :Number, total :Number) :void
    {
        // nada, by default
    }

    /**
     * Called when we've started loading new media. Will not be called
     * for new media that does not require loading.
     */
    protected function startedLoading () :void
    {
        // nada
    }

    /**
     * Called when we've stopped loading, which may be as a result of
     * completion, an error while loading, or early termination.
     */
    protected function stoppedLoading () :void
    {
        // nada
    }

    /** The unaltered URL of the content we're displaying. */
    protected var _url :String;

    /** The unscaled width of our content. */
    protected var _w :int;

    /** The unscaled height of our content. */
    protected var _h :int;

    /** Either a Loader or a VideoDisplay. */
    protected var _media :DisplayObject;

    /** If we're using a Loader, true once the INIT property has been dispatched. */
    protected var _initialized :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.flash {

//import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Shape;
import flash.display.Sprite;

import flash.errors.IOError;

import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.events.StatusEvent;
import flash.events.TextEvent;

import flash.geom.Point;

import flash.text.TextField;
import flash.text.TextFieldAutoSize;

import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;

import flash.net.URLRequest;

import com.threerings.util.StringUtil;
import com.threerings.util.ValueEvent;
import com.threerings.util.Util;

/**
 * Dispatched when the size of the media being loaded is known.
 *
 * @eventType com.threerings.flash.MediaContainer.SIZE_KNOWN
 */
[Event(name="mediaSizeKnown", type="com.threerings.util.ValueEvent")]

/**
 * A wrapper class for all media that will be placed on the screen.
 * Subject to change.
 */
public class MediaContainer extends Sprite
{
    /** A ValueEvent we dispatch when our size is known.
     * Value: [ width, height ].
     *
     * @eventType mediaSizeKnown
     */
    public static const SIZE_KNOWN :String = "mediaSizeKnown";

    /** A log instance that can be shared by sprites. */
    protected static const log :Log = Log.getLog(MediaContainer);

    /**
     * Constructor.
     */
    public function MediaContainer (url :String = null)
    {
        if (url != null) {
            setMedia(url);
        }

        mouseEnabled = false;
        mouseChildren = true;
    }

    /**
     * Return true if the content has been initialized.
     * For most content, this is true if the media is non-null, but
     * for anything loaded with a Loader, it is only true after the INIT event is dispatched.
     */
    public function isContentInitialized () :Boolean
    {
        return (_media != null) && (_initialized || !(_media is Loader));
    }

    /**
     * Get the media. If the media was loaded using a URL, this will
     * likely be the Loader object holding the real media.
     */
    public function getMedia () :DisplayObject
    {
        return _media;
    }

    /**
     * Configure the media to display.
     */
    public function setMedia (url :String) :void
    {
        if (Util.equals(_url, url)) {
            return; // no change
        }
        _url = url;

        // shutdown any previous media
        if (_media != null) {
            shutdown(false);
        }

        // set up the new media
        willShowNewMedia();
        if (StringUtil.endsWith(url.toLowerCase(), ".flv")) {
            setupVideo(url);

        } else {
            setupSwfOrImage(url);
        }
    }

    /**
     * Configure our media as an instance of the specified class.
     */
    public function setMediaClass (clazz :Class) :void
    {
        setMediaObject(new clazz() as DisplayObject);
    }

    /**
     * Configure an already-instantiated DisplayObject as our media.
     */
    public function setMediaObject (disp :DisplayObject) :void
    {
        _url = null;
        if (_media != null) {
            shutdown(false);
        }

        willShowNewMedia();
        addChild(disp);
        _media = disp;
        updateContentDimensions(disp.width, disp.height);
    }

    /**
     * A place where subclasses can initialize things prior to showing
     * new media.
     */
    protected function willShowNewMedia () :void
    {
        _initialized = false;
    }

    /**
     * Configure this sprite to show a video.
     */
    protected function setupVideo (url :String) :void
    {
        var vid :VideoDisplayer = new VideoDisplayer();
        vid.addEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
        vid.addEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
        _media = vid;
        addChild(vid);
        vid.setup(url);
    }

    /**
     * Configure this sprite to show an image or flash movie.
     */
    protected function setupSwfOrImage (url :String) :void
    {
        startedLoading();

        // create our loader and set up some event listeners
        var loader :Loader = new Loader();
        _media = loader;
        var info :LoaderInfo = loader.contentLoaderInfo;
        info.addEventListener(Event.COMPLETE, loadingComplete);
        info.addEventListener(Event.INIT, handleInit);
        info.addEventListener(IOErrorEvent.IO_ERROR, loadError);
        info.addEventListener(ProgressEvent.PROGRESS, loadProgress);

        // create a mask to prevent the media from drawing out of bounds
        if (getMaxContentWidth() < int.MAX_VALUE &&
                getMaxContentHeight() < int.MAX_VALUE) {
            configureMask(getMaxContentWidth(), getMaxContentHeight());
        }

        // start it loading, add it as a child
        loader.load(new URLRequest(url), getContext(url));
        addChild(loader);

        try {
            updateContentDimensions(info.width, info.height);
        } catch (err :Error) {
            // an error is thrown trying to access these props before they're
            // ready
        }
    }

    /**
     * Display a 'broken image' to indicate there were troubles with
     * loading the media.
     */
    protected function setupBrokenImage (w :int = -1, h :int = -1) :void
    {
        if (w == -1) {
            w = 100;
        }
        if (h == -1) {
            h = 100;
        }
        setMediaObject(ImageUtil.createErrorImage(w, h));
    }

    /**
     * Get the application domain being used by this media, or null if
     * none or not applicable.
     */
    public function getApplicationDomain () :ApplicationDomain
    {
        return (_media is Loader)
            ? (_media as Loader).contentLoaderInfo.applicationDomain
            : null;
    }

    /**
     * Unload the media we're displaying, clean up any resources.
     *
     * @param completely if true, we're going away and should stop
     * everything. Otherwise, we're just loading up new media.
     */
    public function shutdown (completely :Boolean = true) :void
    {
        try {
            // remove the mask (but only if we added it)
            if (_media != null && _media.mask != null) {
                try {
                    removeChild(_media.mask);
                    _media.mask = null;

                } catch (argErr :ArgumentError) {
                    // If we catch this error, it was thrown in removeChild
                    // and means that we did not add the media's mask,
                    // and so shouldn't remove it either. The action we
                    // take here is NOT setting _media.mask = null.
                    // Then, we continue happily...
                }
            }

            if (_media is Loader) {
                var loader :Loader = (_media as Loader);
                var url :String = loader.contentLoaderInfo.url;

                // remove any listeners
                removeListeners(loader.contentLoaderInfo);

                // dispose of media
                try {
                    loader.close();
                } catch (ioe :IOError) {
                    // ignore
                }
                loader.unload();

                removeChild(loader);
                log.info("Unloaded media [url=" + url + "].");

            } else if (_media is VideoDisplayer) {
                var vid :VideoDisplayer = (_media as VideoDisplayer);
                vid.removeEventListener(VideoDisplayer.SIZE_KNOWN, handleVideoSizeKnown);
                vid.removeEventListener(VideoDisplayer.VIDEO_ERROR, handleVideoError);
                vid.shutdown();
                removeChild(vid);

            } else if (_media != null) {
                removeChild(_media);
            }
        } catch (ioe :IOError) {
            log.warning("Error shutting down media: " + ioe);
            log.logStackTrace(ioe);
        }

        // clean everything up
        _w = 0;
        _h = 0;
        _media = null;
    }

    /**
     * Get the width of the content, bounded by the maximum.
     */
    public function getContentWidth () :int
    {
        return Math.min(Math.abs(_w * getMediaScaleX()), getMaxContentWidth());
    }

    /**
     * Get the height of the content, bounded by the maximum.
     */
    public function getContentHeight () :int
    {
        return Math.min(Math.abs(_h * getMediaScaleY()), getMaxContentHeight());
    }

    /**
     * Get the maximum allowable width for our content.
     */
    public function getMaxContentWidth () :int
    {
        return int.MAX_VALUE;
    }

    /**
     * Get the maximum allowable height for our content.
     */
    public function getMaxContentHeight () :int
    {
        return int.MAX_VALUE;
    }

    /**
     * Get the X scaling factor to use on the actual media.
     */
    public function getMediaScaleX () :Number
    {
        return 1;
    }

    /**
     * Get the Y scaling factor to use on the actual media.
     */
    public function getMediaScaleY () :Number
    {
        return 1;
    }

    /**
     * Called by MediaWrapper as notification that its size has changed.
     */
    public function containerDimensionsUpdated (newWidth :Number, newHeight :Number) :void
    {
        // do nothing in base MediaContainer
    }

    override public function toString () :String
    {
        return "MediaContainer[url=" + _url + "]";
    }

    /**
     * Return the LoaderContext that should be used to load the media
     * at the specified url.
     */
    protected function getContext (url :String) :LoaderContext
    {
        if (isImage(url)) {
            // load images into our domain so that we can view their pixels
            return new LoaderContext(true,
                new ApplicationDomain(ApplicationDomain.currentDomain),
                SecurityDomain.currentDomain);

        } else {
            // share nothing, trust nothing
            return new LoaderContext(false, new ApplicationDomain(null), null);
        }
    }

    /**
     * Does the specified url represent an image?
     */
    protected function isImage (url :String) :Boolean
    {
        // look at the last 4 characters in the lowercased url
        switch (url.toLowerCase().slice(-4)) {
        case ".png":
        case ".jpg":
        case ".gif":
            return true;

        default:
            return false;
        }
    }

    /**
     * Remove our listeners from the LoaderInfo object.
     */
    protected function removeListeners (info :LoaderInfo) :void
    {
        info.removeEventListener(Event.COMPLETE, loadingComplete);
        info.removeEventListener(Event.INIT, handleInit);
        info.removeEventListener(IOErrorEvent.IO_ERROR, loadError);
        info.removeEventListener(ProgressEvent.PROGRESS, loadProgress);
    }

    /**
     * A callback to receive IO_ERROR events.
     */
    protected function loadError (event :IOErrorEvent) :void
    {
        stoppedLoading();
        setupBrokenImage(-1, -1);
    }

    /**
     * A callback to receive PROGRESS events.
     */
    protected function loadProgress (event :ProgressEvent) :void
    {
        updateLoadingProgress(event.bytesLoaded, event.bytesTotal);
        var info :LoaderInfo = (event.target as LoaderInfo);
        try {
            updateContentDimensions(info.width, info.height);
        } catch (err :Error) {
            // an error is thrown trying to access these props before they're
            // ready
        }
    }

    /**
     * Handles video size known.
     */
    protected function handleVideoSizeKnown (event :ValueEvent) :void
    {
        var args :Array = (event.value as Array);
        updateContentDimensions(int(args[0]), int(args[1]));
    }

    /**
     * Handles an error loading a video.
     */
    protected function handleVideoError (event :ValueEvent) :void
    {
        log.warning("Error loading video [cause=" + event.value + "].");
        stoppedLoading();
        setupBrokenImage(-1, -1);
    }

    /**
     * Handles the INIT event for content loaded with a Loader.
     */
    protected function handleInit (event :Event) :void
    {
        _initialized = true;
    }

    /**
     * Callback function to receive COMPLETE events for swfs or images.
     */
    protected function loadingComplete (event :Event) :void
    {
        var info :LoaderInfo = (event.target as LoaderInfo);
        removeListeners(info);

//        trace("Loading complete: " + info.url +
//            ", childAllowsParent=" + info.childAllowsParent +
//            ", parentAllowsChild=" + info.parentAllowsChild +
//            ", sameDomain=" + info.sameDomain);

        updateContentDimensions(info.width, info.height);
        updateLoadingProgress(1, 1);
        stoppedLoading();
    }

    /**
     * Configure the mask for this object.
     */
    protected function configureMask (ww :int, hh :int) :void
    {
        var mask :Shape;
        if (_media.mask != null) {
            // see if the mask was added by us
            try {
                getChildIndex(_media.mask);

            } catch (argErr :ArgumentError) {
                // oy! We are not the controllers of this mask, it must
                // have been added by someone else. This probably means
                // that the _media is not a Loader, and so we should just
                // leave it alone with its custom mask.
                return;
            }

            // otherwise, it's a mask we previously configured
            mask = (_media.mask as Shape);

        } else {
            mask = new Shape();
            // the mask must be added to the display list (which is wacky)
            addChild(mask);
            _media.mask = mask;
        }

        mask.graphics.clear();
        mask.graphics.beginFill(0xFFFFFF);
        mask.graphics.drawRect(0, 0, ww, hh);
        mask.graphics.endFill();
    }

    /**
     * Called during loading as we figure out how big the content we're
     * loading is.
     */
    protected function updateContentDimensions (ww :int, hh :int) :void
    {
        // update our saved size, and possibly notify our container
        if (_w != ww || _h != hh) {
            _w = ww;
            _h = hh;
            contentDimensionsUpdated();
            // dispatch an event to any listeners
            dispatchEvent(new ValueEvent(SIZE_KNOWN, [ ww, hh ]));
        }
    }

    /**
     * Called when we know the true size of the content.
     */
    protected function contentDimensionsUpdated () :void
    {
        // nada, by default
    }

    /**
     * Update the graphics to indicate how much is loaded.
     */
    protected function updateLoadingProgress (
            soFar :Number, total :Number) :void
    {
        // nada, by default
    }

    /**
     * Called when we've started loading new media. Will not be called
     * for new media that does not require loading.
     */
    protected function startedLoading () :void
    {
        // nada
    }

    /**
     * Called when we've stopped loading, which may be as a result of
     * completion, an error while loading, or early termination.
     */
    protected function stoppedLoading () :void
    {
        // nada
    }

    /** The unaltered URL of the content we're displaying. */
    protected var _url :String;

    /** The unscaled width of our content. */
    protected var _w :int;

    /** The unscaled height of our content. */
    protected var _h :int;

    /** Either a Loader or a VideoDisplay. */
    protected var _media :DisplayObject;

    /** If we're using a Loader, true once the INIT event has been dispatched. */
    protected var _initialized :Boolean;
}
}

Comment fixup.

Comment fixup.

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


package justpinegames.Logi
{
	import com.gskinner.motion.GTweener;
	import flash.events.Event;
	import flash.desktop.Clipboard;
	import flash.desktop.ClipboardFormats;
	import flash.utils.getQualifiedClassName;
	import feathers.display.Sprite;
	import feathers.controls.Button;
	import feathers.controls.List;
import feathers.controls.Scroller;
import feathers.controls.renderers.IListItemRenderer;
	import feathers.controls.ScrollContainer;
    import feathers.controls.text.BitmapFontTextRenderer;
    import feathers.core.FeathersControl;
	import feathers.data.ListCollection;
	import feathers.layout.VerticalLayout;
	import feathers.text.BitmapFontTextFormat;
	import starling.core.Starling;
	import starling.display.Quad;
	import starling.events.Event;
	import starling.text.BitmapFont;
	import starling.textures.TextureSmoothing;

	/**
	 * Main class, used to display console and handle its events.
	 */
	public class Console extends Sprite
	{
		private static var _console:Console;
		private static var _archiveOfUndisplayedLogs:Array = [];

		private var _consoleSettings:ConsoleSettings;
		private var _defaultFont:BitmapFont;
		private var _format:BitmapFontTextFormat;
		private var _formatBackground:BitmapFontTextFormat;
		private var _consoleContainer:Sprite;
		private var _hudContainer:ScrollContainer;
		private var _consoleHeight:Number;
		private var _isShown:Boolean;
		private var _copyButton:Button;
		private var _data:Array;
		private var _quad:Quad;
		private var _list:List;

		private const VERTICAL_PADDING: Number = 5;
		private const HORIZONTAL_PADDING: Number = 5;

		/**
		 * You need to create the instance of this class and add it to the stage in order to use this library.
		 *
		 * @param	consoleSettings   Optional parameter which can specify the look and behaviour of the console.
		 */
		public function Console(consoleSettings:ConsoleSettings = null)
		{
			_consoleSettings = consoleSettings ? consoleSettings : new ConsoleSettings();

			_console = _console ? _console : this;

			_data = [];

			_defaultFont = new BitmapFont();
			_format = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.textColor);
			_format.letterSpacing = 2;
			_formatBackground = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.textBackgroundColor);
			_formatBackground.letterSpacing = 2;

			this.addEventListener(starling.events.Event.ADDED_TO_STAGE, addedToStageHandler);
		}

		public function get isShown():Boolean
		{
			return _isShown;
		}

		public function set isShown(value:Boolean):void
		{
			if (_isShown == value)
			{
				return;
			}

			_isShown = value;

			if (_isShown)
			{
				show();
			}
			else
			{
				hide();
			}
		}

		private function addedToStageHandler(e:starling.events.Event):void
		{
			_consoleHeight = this.stage.stageHeight * _consoleSettings.consoleSize;

			_isShown = false;

			_consoleContainer = new FeathersControl();
			_consoleContainer.alpha = 0;
			_consoleContainer.y = -_consoleHeight;
			this.addChild(_consoleContainer);

			_quad = new Quad(this.stage.stageWidth, _consoleHeight, _consoleSettings.consoleBackground);
			_quad.alpha = _consoleSettings.consoleTransparency;
			_consoleContainer.addChild(_quad);

			// TODO Make the list selection work correctly.
			_list = new List();
			_list.x = HORIZONTAL_PADDING;
			_list.y = VERTICAL_PADDING;
			_list.dataProvider = new ListCollection(_data);
			_list.itemRendererFactory = function():IListItemRenderer
			{
				var consoleItemRenderer:ConsoleItemRenderer = new ConsoleItemRenderer(_consoleSettings.textColor, _consoleSettings.highlightColor);
				consoleItemRenderer.width = _list.width;
				consoleItemRenderer.height = 20;
				return consoleItemRenderer;
			};
			_list.onChange.add(copyLine);
			_consoleContainer.addChild(_list);

			_copyButton = new Button();

			_copyButton.label = "Copy All";
			_copyButton.addEventListener(starling.events.Event.ADDED, function(e:starling.events.Event):void
			{
                _copyButton.defaultLabelProperties.smoothing = TextureSmoothing.NONE;
                _copyButton.downLabelProperties.smoothing = TextureSmoothing.NONE;

				_copyButton.defaultLabelProperties.textFormat = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.textColor);
				_copyButton.downLabelProperties.textFormat = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.highlightColor);

                _copyButton.stateToSkinFunction = function(target:Object, state:Object, oldValue:Object = null):Object
                {
                    return null;
                };

				_copyButton.width = 150;
				_copyButton.height = 40;
			});
			_copyButton.onPress.add(copy);
			_consoleContainer.addChild(_copyButton);

			_hudContainer = new ScrollContainer();
			// TODO This should be changed to prevent the hud from even creating, not just making it invisible.
			if (!_consoleSettings.hudEnabled)
			{
				_hudContainer.visible = false;
			}
			_hudContainer.x = HORIZONTAL_PADDING;
			_hudContainer.y = VERTICAL_PADDING;
			_hudContainer.touchable = false;
			_hudContainer.layout = new VerticalLayout();
			_hudContainer.scrollerProperties.verticalScrollPolicy = Scroller.SCROLL_POLICY_OFF;
			this.addChild(_hudContainer);

			this.setScreenSize(Starling.current.nativeStage.stageWidth, Starling.current.nativeStage.stageHeight);

			for each (var undisplayedMessage:* in _archiveOfUndisplayedLogs)
			{
				this.logMessage(undisplayedMessage);
			}

			_archiveOfUndisplayedLogs = [];

			Starling.current.nativeStage.addEventListener(flash.events.Event.RESIZE, function(e:flash.events.Event):void
			{
				setScreenSize(Starling.current.nativeStage.stageWidth, Starling.current.nativeStage.stageHeight);
			});
		}

		private function setScreenSize(width:Number, height:Number):void
		{
			_consoleContainer.width = width;
			_consoleContainer.height = height;

			_consoleHeight = height * _consoleSettings.consoleSize;

			_quad.width = width;
			_quad.height = _consoleHeight;

			_copyButton.x = width - 110 - HORIZONTAL_PADDING;
			_copyButton.y = _consoleHeight - 33 - VERTICAL_PADDING;

			_list.width = this.stage.stageWidth - HORIZONTAL_PADDING * 2;
			_list.height = _consoleHeight - VERTICAL_PADDING * 2;

			if (!_isShown)
			{
				_consoleContainer.y = -_consoleHeight;
			}
		}

		private function show():void
		{
			_consoleContainer.visible = true;

			GTweener.to(_consoleContainer, _consoleSettings.animationTime, { y: 0, alpha: 1 } );
			GTweener.to(_hudContainer, _consoleSettings.animationTime, { alpha: 0 } );

			_isShown = true;
		}

		private function hide():void
		{
			GTweener.to(_consoleContainer, _consoleSettings.animationTime, { y: -_consoleHeight, alpha: 0 }).onComplete = function():void
			{
				_consoleContainer.visible = false;
			};

			GTweener.to(_hudContainer, _consoleSettings.animationTime, { alpha: 1 } );

			_isShown = false;
		}

		private function copyLine(list:List):void
		{
			//log(list.selectedItem.data);
		}

		/**
		 * You can use this data to save a log to the file.
		 *
		 * @return  Log messages joined into a String with new lines.
		 */
		public function getLogData():String
		{
			var text:String = "";

			for each (var object:Object in _data)
			{
				text += object.data + "\n";
			}

			return text;
		}

		private function copy(button:Button):void
		{
			var text:String = this.getLogData();
			Clipboard.generalClipboard.setData(ClipboardFormats.TEXT_FORMAT, text);
		}

		/**
		 * Displays the message string in the console, or on the HUD if the console is hidden.
		 *
		 * @param	message   String to display
		 */
		public function logMessage(message:String):void
		{
			if (_consoleSettings.traceEnabled)
			{
				trace(message);
			}

			var labelDisplay: String = (new Date()).toLocaleTimeString() + ": " + message;

			_list.dataProvider.push({label: labelDisplay, data: message});

			var createLabel:Function = function(text:String, format:BitmapFontTextFormat):BitmapFontTextRenderer
			{
				var label:BitmapFontTextRenderer = new BitmapFontTextRenderer();
				label.addEventListener(starling.events.Event.ADDED, function(e:starling.events.Event):void
				{
					label.textFormat = format;
				});
				label.smoothing = TextureSmoothing.NONE;
				label.text = text;
				label.validate();
				return label;
			};

			var hudLabelContainer:FeathersControl = new FeathersControl();
			hudLabelContainer.width = 640;
			hudLabelContainer.height = 20;

			var addBackground:Function = function(offsetX:int, offsetY: int):void
			{
				var hudLabelBackground:BitmapFontTextRenderer = createLabel(message, _formatBackground);
				hudLabelBackground.x = offsetX;
				hudLabelBackground.y = offsetY;
				hudLabelContainer.addChild(hudLabelBackground);
			};

			addBackground(0, 0);
			addBackground(2, 0);
			addBackground(0, 2);
			addBackground(2, 2);

			var hudLabel:BitmapFontTextRenderer = createLabel(message, _format);
			hudLabel.x += 1;
			hudLabel.y += 1;
			hudLabelContainer.addChild(hudLabel);

			_hudContainer.addChildAt(hudLabelContainer, 0);

			GTweener.to(hudLabelContainer, _consoleSettings.hudMessageFadeOutTime, { alpha: 0 }, { delay: _consoleSettings.hudMessageDisplayTime } ).onComplete = function():void
			{
				_hudContainer.removeChild(hudLabelContainer);
			};

			// TODO use the correct API, currently there is a problem with List max vertical position. A bug in foxhole?
			_list.verticalScrollPosition = Math.max(_list.dataProvider.length * 20 - _list.height, 0);

		}

		/**
		 * Returns the fist created Console instance.
		 *
		 * @return Console instance
		 */
		public static function getMainConsoleInstance():Console
		{
			return _console;
		}

		/**
		* Main log function. Usage is the same as for the trace statement.
		*
		* For data sent to the log function to be displayed, you need to first create a LogConsole instance, and add it to the Starling stage.
		*
		* @param	... arguments   Variable number of arguments, which will be displayed in the log
		*/
		public static function staticLogMessage(... arguments):void
		{
			var message:String = "";
			var firstTime:Boolean = true;

			for each (var argument:* in arguments)
			{
				var description:String;

				if (argument == null)
				{
					description = "[null]"
				}
				else if (!("toString" in argument))
				{
					description = "[object " + getQualifiedClassName(argument) + "]";
				}
				else
				{
					description = argument;
				}

				if (firstTime)
				{
					message = description;
					firstTime = false;
				}
				else
				{
					message += ", " + description;
				}
			}

			if (Console.getMainConsoleInstance() == null)
			{
				_archiveOfUndisplayedLogs.push(message);
			}
			else
			{
				Console.getMainConsoleInstance().logMessage(message);
			}
		}
	}
}

package justpinegames.Logi
{
	import flash.events.Event;
	import flash.desktop.Clipboard;
	import flash.desktop.ClipboardFormats;
	import flash.utils.getQualifiedClassName;
	import feathers.display.Sprite;
	import feathers.controls.Button;
	import feathers.controls.List;
	import feathers.controls.Scroller;
	import feathers.controls.renderers.IListItemRenderer;
	import feathers.controls.ScrollContainer;
    import feathers.controls.text.BitmapFontTextRenderer;
    import feathers.core.FeathersControl;
	import feathers.data.ListCollection;
	import feathers.layout.VerticalLayout;
	import feathers.text.BitmapFontTextFormat;
	import starling.animation.Tween;
	import starling.core.Starling;
	import starling.display.Quad;
	import starling.events.Event;
	import starling.text.BitmapFont;
	import starling.textures.TextureSmoothing;

	/**
	 * Main class, used to display console and handle its events.
	 */
	public class Console extends Sprite
	{
		private static var _console:Console;
		private static var _archiveOfUndisplayedLogs:Array = [];

		private var _consoleSettings:ConsoleSettings;
		private var _defaultFont:BitmapFont;
		private var _format:BitmapFontTextFormat;
		private var _formatBackground:BitmapFontTextFormat;
		private var _consoleContainer:Sprite;
		private var _hudContainer:ScrollContainer;
		private var _consoleHeight:Number;
		private var _isShown:Boolean;
		private var _copyButton:Button;
		private var _data:Array;
		private var _quad:Quad;
		private var _list:List;

		private const VERTICAL_PADDING: Number = 5;
		private const HORIZONTAL_PADDING: Number = 5;

		/**
		 * You need to create the instance of this class and add it to the stage in order to use this library.
		 *
		 * @param	consoleSettings   Optional parameter which can specify the look and behaviour of the console.
		 */
		public function Console(consoleSettings:ConsoleSettings = null)
		{
			_consoleSettings = consoleSettings ? consoleSettings : new ConsoleSettings();

			_console = _console ? _console : this;

			_data = [];

			_defaultFont = new BitmapFont();
			_format = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.textColor);
			_format.letterSpacing = 2;
			_formatBackground = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.textBackgroundColor);
			_formatBackground.letterSpacing = 2;

			this.addEventListener(starling.events.Event.ADDED_TO_STAGE, addedToStageHandler);
		}

		public function get isShown():Boolean
		{
			return _isShown;
		}

		public function set isShown(value:Boolean):void
		{
			if (_isShown == value)
			{
				return;
			}

			_isShown = value;

			if (_isShown)
			{
				show();
			}
			else
			{
				hide();
			}
		}

		private function addedToStageHandler(e:starling.events.Event):void
		{
			_consoleHeight = this.stage.stageHeight * _consoleSettings.consoleSize;

			_isShown = false;

			_consoleContainer = new FeathersControl();
			_consoleContainer.alpha = 0;
			_consoleContainer.y = -_consoleHeight;
			this.addChild(_consoleContainer);

			_quad = new Quad(this.stage.stageWidth, _consoleHeight, _consoleSettings.consoleBackground);
			_quad.alpha = _consoleSettings.consoleTransparency;
			_consoleContainer.addChild(_quad);

			// TODO Make the list selection work correctly.
			_list = new List();
			_list.x = HORIZONTAL_PADDING;
			_list.y = VERTICAL_PADDING;
			_list.dataProvider = new ListCollection(_data);
			_list.itemRendererFactory = function():IListItemRenderer
			{
				var consoleItemRenderer:ConsoleItemRenderer = new ConsoleItemRenderer(_consoleSettings.textColor, _consoleSettings.highlightColor);
				consoleItemRenderer.width = _list.width;
				consoleItemRenderer.height = 20;
				return consoleItemRenderer;
			};
			_list.addEventListener(starling.events.Event.CHANGE, copyLine);
			_consoleContainer.addChild(_list);

			_copyButton = new Button();

			_copyButton.label = "Copy All";
			_copyButton.addEventListener(starling.events.Event.ADDED, function(e:starling.events.Event):void
			{
                _copyButton.defaultLabelProperties.smoothing = TextureSmoothing.NONE;
                _copyButton.downLabelProperties.smoothing = TextureSmoothing.NONE;

				_copyButton.defaultLabelProperties.textFormat = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.textColor);
				_copyButton.downLabelProperties.textFormat = new BitmapFontTextFormat(_defaultFont, 16, _consoleSettings.highlightColor);

                _copyButton.stateToSkinFunction = function(target:Object, state:Object, oldValue:Object = null):Object
                {
                    return null;
                };

				_copyButton.width = 150;
				_copyButton.height = 40;
			});
			_copyButton.addEventListener(starling.events.Event.SELECT, copy);
			_consoleContainer.addChild(_copyButton);

			_hudContainer = new ScrollContainer();
			// TODO This should be changed to prevent the hud from even creating, not just making it invisible.
			if (!_consoleSettings.hudEnabled)
			{
				_hudContainer.visible = false;
			}
			_hudContainer.x = HORIZONTAL_PADDING;
			_hudContainer.y = VERTICAL_PADDING;
			_hudContainer.touchable = false;
			_hudContainer.layout = new VerticalLayout();
			_hudContainer.scrollerProperties.verticalScrollPolicy = Scroller.SCROLL_POLICY_OFF;
			this.addChild(_hudContainer);

			this.setScreenSize(Starling.current.nativeStage.stageWidth, Starling.current.nativeStage.stageHeight);

			for each (var undisplayedMessage:* in _archiveOfUndisplayedLogs)
			{
				this.logMessage(undisplayedMessage);
			}

			_archiveOfUndisplayedLogs = [];

			Starling.current.nativeStage.addEventListener(flash.events.Event.RESIZE, function(e:flash.events.Event):void
			{
				setScreenSize(Starling.current.nativeStage.stageWidth, Starling.current.nativeStage.stageHeight);
			});
		}

		private function setScreenSize(width:Number, height:Number):void
		{
			_consoleContainer.width = width;
			_consoleContainer.height = height;

			_consoleHeight = height * _consoleSettings.consoleSize;

			_quad.width = width;
			_quad.height = _consoleHeight;

			_copyButton.x = width - 110 - HORIZONTAL_PADDING;
			_copyButton.y = _consoleHeight - 33 - VERTICAL_PADDING;

			_list.width = this.stage.stageWidth - HORIZONTAL_PADDING * 2;
			_list.height = _consoleHeight - VERTICAL_PADDING * 2;

			if (!_isShown)
			{
				_consoleContainer.y = -_consoleHeight;
			}
		}

		private function show():void
		{
			_consoleContainer.visible = true;

			var __tween1:Tween = new Tween(_consoleContainer, _consoleSettings.animationTime);
			__tween1.animate("y", 0);
			__tween1.fadeTo(1);
			Starling.juggler.add(__tween1);

			var __tween2:Tween = new Tween(_hudContainer, _consoleSettings.animationTime);
			__tween2.fadeTo(0);
			Starling.juggler.add(__tween2);

			_isShown = true;
		}

		private function hide():void
		{
			var __tween1:Tween = new Tween(_consoleContainer, _consoleSettings.animationTime);
			__tween1.animate("y",  -_consoleHeight);
			__tween1.fadeTo(0);
			__tween1.onComplete = function():void
			{
				_consoleContainer.visible = false;
			};
			Starling.juggler.add(__tween1);

			var __tween2:Tween = new Tween(_hudContainer, _consoleSettings.animationTime);
			__tween2.fadeTo(1);
			Starling.juggler.add(__tween2);

			_isShown = false;
		}

		private function copyLine(list:List):void
		{
			//log(list.selectedItem.data);
		}

		/**
		 * You can use this data to save a log to the file.
		 *
		 * @return  Log messages joined into a String with new lines.
		 */
		public function getLogData():String
		{
			var text:String = "";

			for each (var object:Object in _data)
			{
				text += object.data + "\n";
			}

			return text;
		}

		private function copy(button:Button):void
		{
			var text:String = this.getLogData();
			Clipboard.generalClipboard.setData(ClipboardFormats.TEXT_FORMAT, text);
		}

		/**
		 * Displays the message string in the console, or on the HUD if the console is hidden.
		 *
		 * @param	message   String to display
		 */
		public function logMessage(message:String):void
		{
			if (_consoleSettings.traceEnabled)
			{
				trace(message);
			}

			var labelDisplay: String = (new Date()).toLocaleTimeString() + ": " + message;

			_list.dataProvider.push({label: labelDisplay, data: message});

			var createLabel:Function = function(text:String, format:BitmapFontTextFormat):BitmapFontTextRenderer
			{
				var label:BitmapFontTextRenderer = new BitmapFontTextRenderer();
				label.addEventListener(starling.events.Event.ADDED, function(e:starling.events.Event):void
				{
					label.textFormat = format;
				});
				label.smoothing = TextureSmoothing.NONE;
				label.text = text;
				label.validate();
				return label;
			};

			var hudLabelContainer:FeathersControl = new FeathersControl();
			hudLabelContainer.width = 640;
			hudLabelContainer.height = 20;

			var addBackground:Function = function(offsetX:int, offsetY: int):void
			{
				var hudLabelBackground:BitmapFontTextRenderer = createLabel(message, _formatBackground);
				hudLabelBackground.x = offsetX;
				hudLabelBackground.y = offsetY;
				hudLabelContainer.addChild(hudLabelBackground);
			};

			addBackground(0, 0);
			addBackground(2, 0);
			addBackground(0, 2);
			addBackground(2, 2);

			var hudLabel:BitmapFontTextRenderer = createLabel(message, _format);
			hudLabel.x += 1;
			hudLabel.y += 1;
			hudLabelContainer.addChild(hudLabel);

			_hudContainer.addChildAt(hudLabelContainer, 0);

			var __tween1:Tween = new Tween(hudLabelContainer, _consoleSettings.hudMessageFadeOutTime);
			__tween1.delay = _consoleSettings.hudMessageDisplayTime
			__tween1.fadeTo(0);
			__tween1.onComplete = function():void
			{
				_hudContainer.removeChild(hudLabelContainer);
			};
			Starling.juggler.add(__tween1);

			// TODO use the correct API, currently there is a problem with List max vertical position. A bug in foxhole?
			_list.verticalScrollPosition = Math.max(_list.dataProvider.length * 20 - _list.height, 0);

		}

		/**
		 * Returns the fist created Console instance.
		 *
		 * @return Console instance
		 */
		public static function getMainConsoleInstance():Console
		{
			return _console;
		}

		/**
		* Main log function. Usage is the same as for the trace statement.
		*
		* For data sent to the log function to be displayed, you need to first create a LogConsole instance, and add it to the Starling stage.
		*
		* @param	... arguments   Variable number of arguments, which will be displayed in the log
		*/
		public static function staticLogMessage(... arguments):void
		{
			var message:String = "";
			var firstTime:Boolean = true;

			for each (var argument:* in arguments)
			{
				var description:String;

				if (argument == null)
				{
					description = "[null]"
				}
				else if (!("toString" in argument))
				{
					description = "[object " + getQualifiedClassName(argument) + "]";
				}
				else
				{
					description = argument;
				}

				if (firstTime)
				{
					message = description;
					firstTime = false;
				}
				else
				{
					message += ", " + description;
				}
			}

			if (Console.getMainConsoleInstance() == null)
			{
				_archiveOfUndisplayedLogs.push(message);
			}
			else
			{
				Console.getMainConsoleInstance().logMessage(message);
			}
		}
	}
}

Update src/justpinegames/Logi/Console.as

Removed GTween dependency bringing this library in line with Feathers