package im.siver.logger.controllers {

    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    import im.siver.logger.models.Constants;
    import im.siver.logger.utils.LoggerUtils;
    import im.siver.logger.views.components.Filter;
    import im.siver.logger.views.components.panels.TracePanel;
    import im.siver.logger.views.components.windows.SnapshotWindow;
    import im.siver.logger.views.components.windows.TraceWindow;

    import mx.collections.ArrayCollection;
    import mx.events.CollectionEvent;
    import mx.events.FlexEvent;
    import mx.events.ListEvent;
    import mx.utils.StringUtil;

    public final class TraceController extends EventDispatcher {

        [Bindable]
        private var _dataFilterd:ArrayCollection = new ArrayCollection();

        private var _data:ArrayCollection = new ArrayCollection();
        private var _panel:TracePanel;
        private var _send:Function;

        /**
         * Save panel
         */
        public function TraceController(panel:TracePanel, send:Function) {
            _panel = panel;
            _send = send;
            _panel.addEventListener(FlexEvent.CREATION_COMPLETE, creationComplete, false, 0, true);
        }

        private function collectionDidChange(e:CollectionEvent):void {
            if (_panel.autoScrollButton.selected) {
                _panel.datagrid.validateNow();
                _panel.datagrid.verticalScrollPosition = _panel.datagrid.maxVerticalScrollPosition;
            }
        }

        /**
         * Panel is ready to link data providers
         */
        private function creationComplete(e:FlexEvent):void {
            _panel.removeEventListener(FlexEvent.CREATION_COMPLETE, creationComplete);
            _panel.datagrid.dataProvider = _dataFilterd;
            _panel.datagrid.addEventListener(ListEvent.ITEM_DOUBLE_CLICK, showTrace, false, 0, true);
            _panel.filter.addEventListener(Filter.CHANGED, filterChanged, false, 0, true);
            _panel.clearButton.addEventListener(MouseEvent.CLICK, clear, false, 0, true);
            _dataFilterd.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionDidChange, false, 0, true);
        }

        /**
         * Show a trace in an output window
         */
        private function filterChanged(event:Event):void {
            _dataFilterd.removeAll();

            // Check if a filter term is given
            if (_panel.filter.words.length == 0) {
                for (var i:int = 0; i < _data.length; i++) {
                    _dataFilterd.addItem(_data[i]);
                }
                return;
            }
            for (var n:int = 0; n < _data.length; n++) {
                if (checkFilter(_data[n])) {
                    _dataFilterd.addItem(_data[n]);
                }
            }
        }

        private function addItem(item:Object):void {
            if (_panel.filter.words.length == 0) {
                _dataFilterd.addItem(item);
                return;
            }
            if (checkFilter(item)) {
                _dataFilterd.addItem(item);
            }
        }

        /**
         * Loop through the search terms and compare strings
         */
        private function checkFilter(item:Object):Boolean {
            var message:String = item.message;
            var target:String = item.target;
            var label:String = item.label;
            var person:String = item.person;
            if (message == null) message = "";
            if (target == null) target = "";
            if (label == null) label = "";
            if (person == null) person = "";
            message = StringUtil.trim(message).toLowerCase();
            target = StringUtil.trim(target).toLowerCase();
            label = StringUtil.trim(label).toLowerCase();
            person = StringUtil.trim(person).toLowerCase();
            var i:int;

            // Clone words
            var words:Array = [];
            for (i = 0; i < _panel.filter.words.length; i++) {
                words[i] = _panel.filter.words[i];
            }

            if (message != "") {
                for (i = 0; i < words.length; i++) {
                    if (message.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            if (target != "") {
                for (i = 0; i < words.length; i++) {
                    if (target.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            if (label != "") {
                for (i = 0; i < words.length; i++) {
                    if (label.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            if (person != "") {
                for (i = 0; i < words.length; i++) {
                    if (person.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            return false;
        }

        /**
         * Show a trace in an output window
         */
        private function showTrace(event:ListEvent):void {
            // Check if the selected item is still available
            if (event.currentTarget.selectedItem != null) {

                // Get the data
                var item:Object = _dataFilterd.getItemAt(event.currentTarget.selectedIndex);

                // Check the window to open
                if (item.message == "Snapshot" && item.xml == null) {
                    var snapshotWindow:SnapshotWindow = new SnapshotWindow();
                    snapshotWindow.setData(item);
                    snapshotWindow.open();
                } else {
                    var traceWindow:TraceWindow = new TraceWindow();
                    traceWindow.setData(item);
                    traceWindow.open();
                }
            }
        }

        /**
         * Clear traces
         */
        public function clear(event:MouseEvent = null):void {
            _data.removeAll();
            _dataFilterd.removeAll();
            _panel.datagrid.horizontalScrollPosition = 0;
        }

        /**
         * Data handler from client
         */
        public function setData(data:Object):void {
            // Vars for loop
            var date:Date;
            var hours:String;
            var minutes:String;
            var seconds:String;
            var miliseconds:String;
            var time:String;
            var memory:String;
            var traceItem:Object;

            switch (data["command"]) {

                case Constants.COMMAND_CLEAR_TRACES:
                    clear();
                    break;

                case Constants.COMMAND_TRACE:

                    // Format the properties
                    date = data["date"];
                    time = zeroPad(date.getHours(), 2) + ":" + zeroPad(date.getMinutes(), 2) + ":" + zeroPad(date.getSeconds(), 2) + "." + zeroPad(date.getMilliseconds(), 3);
                    memory = Math.round(data["memory"] / 1024) + " Kb";

                    // Create the trace object
                    traceItem = {};

                    // Check the label
                    if (data["xml"]..node.length() > 1 && data["xml"]..node.length() <= 3) {
                        if (data["xml"].node[0].@type == Constants.TYPE_STRING || data["xml"].node[0].@type == Constants.TYPE_BOOLEAN || data["xml"].node[0].@type == Constants.TYPE_NUMBER || data["xml"].node[0].@type == Constants.TYPE_INT || data["xml"].node[0].@type == Constants.TYPE_UINT) {
                            traceItem.message = LoggerUtils.stripBreaks(LoggerUtils.htmlUnescape(data["xml"].node.children()[0].@label));
                        } else {
                            traceItem.message = LoggerUtils.stripBreaks(LoggerUtils.htmlUnescape(data["xml"].node.@label)) + " ...";
                        }
                    } else {
                        traceItem.message = LoggerUtils.stripBreaks(LoggerUtils.htmlUnescape(data["xml"].node.@label)) + " ...";
                    }

                    // Clean target
                    if (data['target'].indexOf('object') != -1) {
                        traceItem.target = String(data['target']).replace('object ', '');
                    } else {
                        traceItem.target = data["target"];
                    }

                    // Add extra info
                    traceItem.line = _data.length + 1;
                    traceItem.time = time;
                    traceItem.memory = memory;
                    traceItem.reference = data["reference"];
                    traceItem.label = data["label"];
                    traceItem.person = data["person"];
                    traceItem.color = data["color"];
                    traceItem.xml = data["xml"];

                    // Add to list
                    _data.addItem(traceItem);
                    addItem(traceItem);
                    break;

                case Constants.COMMAND_SNAPSHOT:

                    // Format the properties
                    date = data["date"];
                    hours = (date.getHours() < 10) ? "0" + date.getHours().toString() : date.getHours().toString();
                    minutes = (date.getMinutes() < 10) ? "0" + date.getMinutes().toString() : date.getMinutes().toString();
                    seconds = (date.getSeconds() < 10) ? "0" + date.getSeconds().toString() : date.getSeconds().toString();
                    miliseconds = date.getMilliseconds().toString();
                    time = hours + ":" + minutes + ":" + seconds + "." + miliseconds;
                    memory = Math.round(data["memory"] / 1024) + " Kb";

                    // Read the bitmap
                    try {
                        var bitmapData:BitmapData = new BitmapData(data["width"], data["height"]);
                        bitmapData.setPixels(new Rectangle(0, 0, data["width"], data["height"]), data["bytes"]);
                    } catch (e:Error) {
                        return;
                    }

                    // Create the trace object
                    traceItem = {};
                    traceItem.line = _data.length + 1;
                    traceItem.time = time;
                    traceItem.memory = memory;
                    traceItem.width = data["width"];
                    traceItem.height = data["height"];
                    traceItem.bitmapData = bitmapData;
                    traceItem.message = "Snapshot";
                    traceItem.target = data["target"];
                    traceItem.label = data["label"];
                    traceItem.person = data["person"];
                    traceItem.color = data["color"];
                    traceItem.xml = null;

                    // Add to list
                    _data.addItem(traceItem);
                    addItem(traceItem);
                    break;
            }
        }

        private function zeroPad(value:int, length:int, pad:String = '0'):String {
            var result:String = String(value);
            while (result.length < length) {
                result = pad + result;
            }
            return result;
        }
    }
}

package im.siver.logger.controllers {

    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    import im.siver.logger.models.Constants;
    import im.siver.logger.utils.LoggerUtils;
    import im.siver.logger.views.components.Filter;
    import im.siver.logger.views.components.panels.TracePanel;
    import im.siver.logger.views.components.windows.SnapshotWindow;
    import im.siver.logger.views.components.windows.TraceWindow;

    import mx.collections.ArrayCollection;
    import mx.events.CollectionEvent;
    import mx.events.FlexEvent;
    import mx.events.ListEvent;
    import mx.utils.StringUtil;

    public final class TraceController extends EventDispatcher {

        [Bindable]
        private var _dataFilterd:ArrayCollection = new ArrayCollection();

        private var _data:ArrayCollection = new ArrayCollection();
        private var _panel:TracePanel;
        private var _send:Function;

        /**
         * Save panel
         */
        public function TraceController(panel:TracePanel, send:Function) {
            _panel = panel;
            _send = send;
            _panel.addEventListener(FlexEvent.CREATION_COMPLETE, creationComplete, false, 0, true);
        }

        private function collectionDidChange(e:CollectionEvent):void {
            if (_panel.autoScrollButton.selected) {
                _panel.datagrid.validateNow();
                _panel.datagrid.verticalScrollPosition = _panel.datagrid.maxVerticalScrollPosition;
            }
        }

        /**
         * Panel is ready to link data providers
         */
        private function creationComplete(e:FlexEvent):void {
            _panel.removeEventListener(FlexEvent.CREATION_COMPLETE, creationComplete);
            _panel.datagrid.dataProvider = _dataFilterd;
            _panel.datagrid.addEventListener(ListEvent.ITEM_DOUBLE_CLICK, showTrace, false, 0, true);
            _panel.filter.addEventListener(Filter.CHANGED, filterChanged, false, 0, true);
            _panel.clearButton.addEventListener(MouseEvent.CLICK, clear, false, 0, true);
            _dataFilterd.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionDidChange, false, 0, true);
        }

        /**
         * Show a trace in an output window
         */
        private function filterChanged(event:Event):void {
            // Check if a filter term is given
            if (_panel.filter.words.length == 0) {
                _dataFilterd.filterFunction = null;
                _dataFilterd.refresh();
            } else {
                _dataFilterd.filterFunction = checkFilter;
                _dataFilterd.refresh();
            }
        }

        private function addItem(item:Object):void {
            _dataFilterd.addItem(item);
        }

        private function simpleFilter(item:Object):Boolean {
            return false;
        }

        /**
         * Loop through the search terms and compare strings
         */
        private function checkFilter(item:Object):Boolean {
            var message:String = item.message;
            var target:String = item.target;
            var label:String = item.label;
            var person:String = item.person;
            if (message == null) message = "";
            if (target == null) target = "";
            if (label == null) label = "";
            if (person == null) person = "";
            message = StringUtil.trim(message).toLowerCase();
            target = StringUtil.trim(target).toLowerCase();
            label = StringUtil.trim(label).toLowerCase();
            person = StringUtil.trim(person).toLowerCase();
            var i:int;

            // Clone words
            var words:Array = [];
            for (i = 0; i < _panel.filter.words.length; i++) {
                words[i] = _panel.filter.words[i];
            }

            if (message != "") {
                for (i = 0; i < words.length; i++) {
                    if (message.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            if (target != "") {
                for (i = 0; i < words.length; i++) {
                    if (target.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            if (label != "") {
                for (i = 0; i < words.length; i++) {
                    if (label.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            if (person != "") {
                for (i = 0; i < words.length; i++) {
                    if (person.indexOf(words[i]) != -1) {
                        words.splice(i, 1);
                        i--;
                    }
                }
            }
            if (words.length == 0) return true;
            return false;
        }

        /**
         * Show a trace in an output window
         */
        private function showTrace(event:ListEvent):void {
            // Check if the selected item is still available
            if (event.currentTarget.selectedItem != null) {

                // Get the data
                var item:Object = _dataFilterd.getItemAt(event.currentTarget.selectedIndex);

                // Check the window to open
                if (item.message == "Snapshot" && item.xml == null) {
                    var snapshotWindow:SnapshotWindow = new SnapshotWindow();
                    snapshotWindow.setData(item);
                    snapshotWindow.open();
                } else {
                    var traceWindow:TraceWindow = new TraceWindow();
                    traceWindow.setData(item);
                    traceWindow.open();
                }
            }
        }

        /**
         * Clear traces
         */
        public function clear(event:MouseEvent = null):void {
            _data.removeAll();
            _dataFilterd.removeAll();
            _panel.datagrid.horizontalScrollPosition = 0;
        }

        /**
         * Data handler from client
         */
        public function setData(data:Object):void {
            // Vars for loop
            var date:Date;
            var hours:String;
            var minutes:String;
            var seconds:String;
            var miliseconds:String;
            var time:String;
            var memory:String;
            var traceItem:Object;

            switch (data["command"]) {

                case Constants.COMMAND_CLEAR_TRACES:
                    clear();
                    break;

                case Constants.COMMAND_TRACE:

                    // Format the properties
                    date = data["date"];
                    time = zeroPad(date.getHours(), 2) + ":" + zeroPad(date.getMinutes(), 2) + ":" + zeroPad(date.getSeconds(), 2) + "." + zeroPad(date.getMilliseconds(), 3);
                    memory = Math.round(data["memory"] / 1024) + " Kb";

                    // Create the trace object
                    traceItem = {};

                    // Check the label
                    if (data["xml"]..node.length() > 1 && data["xml"]..node.length() <= 3) {
                        if (data["xml"].node[0].@type == Constants.TYPE_STRING || data["xml"].node[0].@type == Constants.TYPE_BOOLEAN || data["xml"].node[0].@type == Constants.TYPE_NUMBER || data["xml"].node[0].@type == Constants.TYPE_INT || data["xml"].node[0].@type == Constants.TYPE_UINT) {
                            traceItem.message = LoggerUtils.stripBreaks(LoggerUtils.htmlUnescape(data["xml"].node.children()[0].@label));
                        } else {
                            traceItem.message = LoggerUtils.stripBreaks(LoggerUtils.htmlUnescape(data["xml"].node.@label)) + " ...";
                        }
                    } else {
                        traceItem.message = LoggerUtils.stripBreaks(LoggerUtils.htmlUnescape(data["xml"].node.@label)) + " ...";
                    }

                    // Clean target
                    if (data['target'].indexOf('object') != -1) {
                        traceItem.target = String(data['target']).replace('object ', '');
                    } else {
                        traceItem.target = data["target"];
                    }

                    // Add extra info
                    traceItem.line = _data.length + 1;
                    traceItem.time = time;
                    traceItem.memory = memory;
                    traceItem.reference = data["reference"];
                    traceItem.label = data["label"];
                    traceItem.person = data["person"];
                    traceItem.color = data["color"];
                    traceItem.xml = data["xml"];

                    // Add to list
                    _data.addItem(traceItem);
                    addItem(traceItem);
                    break;

                case Constants.COMMAND_SNAPSHOT:

                    // Format the properties
                    date = data["date"];
                    hours = (date.getHours() < 10) ? "0" + date.getHours().toString() : date.getHours().toString();
                    minutes = (date.getMinutes() < 10) ? "0" + date.getMinutes().toString() : date.getMinutes().toString();
                    seconds = (date.getSeconds() < 10) ? "0" + date.getSeconds().toString() : date.getSeconds().toString();
                    miliseconds = date.getMilliseconds().toString();
                    time = hours + ":" + minutes + ":" + seconds + "." + miliseconds;
                    memory = Math.round(data["memory"] / 1024) + " Kb";

                    // Read the bitmap
                    try {
                        var bitmapData:BitmapData = new BitmapData(data["width"], data["height"]);
                        bitmapData.setPixels(new Rectangle(0, 0, data["width"], data["height"]), data["bytes"]);
                    } catch (e:Error) {
                        return;
                    }

                    // Create the trace object
                    traceItem = {};
                    traceItem.line = _data.length + 1;
                    traceItem.time = time;
                    traceItem.memory = memory;
                    traceItem.width = data["width"];
                    traceItem.height = data["height"];
                    traceItem.bitmapData = bitmapData;
                    traceItem.message = "Snapshot";
                    traceItem.target = data["target"];
                    traceItem.label = data["label"];
                    traceItem.person = data["person"];
                    traceItem.color = data["color"];
                    traceItem.xml = null;

                    // Add to list
                    _data.addItem(traceItem);
                    addItem(traceItem);
                    break;
            }
        }

        private function zeroPad(value:int, length:int, pad:String = '0'):String {
            var result:String = String(value);
            while (result.length < length) {
                result = pad + result;
            }
            return result;
        }
    }
}

Use of native filter

Use of native filter


class LightsOutScript {
    Hub @hub;
    Entity @self;
    Entity @board;
    Entity @rightController;
    array<Entity@> buttons(25);
    array<bool> buttonStates(25);
    int numPressedButtons = 0;
    bool gameWon = false;
    bool isPressed = false;

    LightsOutScript(Entity @entity){
        @hub = Managers();
        @self = @entity;
        @board = GetEntityByGUID(1511530025);
        @rightController = GetEntityByGUID(1508919758);

        for (int row = 0; row < 5; ++row) {
            for (int column = 0; column < 5; ++column) {
                Entity @btn = board.GetChild("btn-" + row + "-" + column);
                @buttons[row * 5 + column] = btn.GetChild("btn-" + row + "-" + column).GetChild("button");
                buttonStates[row * 5 + column] = false;
            }
        }

        RegisterUpdate();
    }

    void Update(float deltaTime) {
        if (isPressed && !Input(Trigger, rightController)) {
            isPressed = false;
        }
    }

    void Toggle(int index) {
        bool pressed = !buttonStates[index];
        buttonStates[index] = pressed;

        if (pressed) {
            buttons[index].position.x = -0.06f;
            numPressedButtons += 1;
        } else {
            buttons[index].position.x = 0.0f;
            numPressedButtons -= 1;
        }
    }

    bool IsValid(int index) {
        return index >= 0 && index <= 24;
    }

    // Index goes first row 0 -> 4, second row 5 -> 9 etc.
    void ButtonPress(int index) {
        if (gameWon) {
            return;
        }

        if (!isPressed && Input(Trigger, rightController)) {
            isPressed = true;

            int left = index - 1;
            if (index % 5 != 0 && IsValid(left)) {
                Toggle(left);
            }

            int right = index + 1;
            if (index % 5 != 4 && IsValid(right)) {
                Toggle(right);
            }

            int up = index - 5;
            if (index > 4 && IsValid(up)) {
                Toggle(up);
            }

            int down = index + 5;
            if (index < 20 && IsValid(down)) {
                Toggle(down);
            }

            Toggle(index);

            if (numPressedButtons == 25) {
                gameWon = true;
                //SendMessage(somewhere);
                print("Won the game of lights out.\n");
            }
        }
    }

    void b_0_0() {
        ButtonPress(0);
    }

    void b_0_1() {
        ButtonPress(1);
    }

    void b_0_2() {
        ButtonPress(2);
    }

    void b_0_3() {
        ButtonPress(3);
    }

    void b_0_4() {
        ButtonPress(4);
    }

    void b_1_0() {
        ButtonPress(5);
    }

    void b_1_1() {
        ButtonPress(6);
    }

    void b_1_2() {
        ButtonPress(7);
    }

    void b_1_3() {
        ButtonPress(8);
    }

    void b_1_4() {
        ButtonPress(9);
    }

    void b_2_0() {
        ButtonPress(10);
    }

    void b_2_1() {
        ButtonPress(11);
    }

    void b_2_2() {
        ButtonPress(12);
    }

    void b_2_3() {
        ButtonPress(13);
    }

    void b_2_4() {
        ButtonPress(14);
    }

    void b_3_0() {
        ButtonPress(15);
    }

    void b_3_1() {
        ButtonPress(16);
    }

    void b_3_2() {
        ButtonPress(17);
    }

    void b_3_3() {
        ButtonPress(18);
    }

    void b_3_4() {
        ButtonPress(19);
    }

    void b_4_0() {
        ButtonPress(20);
    }

    void b_4_1() {
        ButtonPress(21);
    }

    void b_4_2() {
        ButtonPress(22);
    }

    void b_4_3() {
        ButtonPress(23);
    }

    void b_4_4() {
        ButtonPress(24);
    }
}

class LightsOutScript {
    Entity @board;
    Entity @rightController;
    array<Entity@> buttons(25);
    array<bool> buttonStates(25);
    int numPressedButtons = 0;
    bool gameWon = false;
    bool isPressed = false;

    LightsOutScript(Entity @entity){
        @board = GetEntityByGUID(1511530025);
        @rightController = GetEntityByGUID(1508919758);

        for (int row = 0; row < 5; ++row) {
            for (int column = 0; column < 5; ++column) {
                Entity @btn = board.GetChild("btn-" + row + "-" + column);
                @buttons[row * 5 + column] = btn.GetChild("btn-" + row + "-" + column).GetChild("button");
                buttonStates[row * 5 + column] = false;
            }
        }

        RegisterUpdate();
    }

    void Update(float deltaTime) {
        if (isPressed && !Input(Trigger, rightController)) {
            isPressed = false;
        }
    }

    void Toggle(int index) {
        bool pressed = !buttonStates[index];
        buttonStates[index] = pressed;

        if (pressed) {
            buttons[index].position.x = -0.06f;
            numPressedButtons += 1;
        } else {
            buttons[index].position.x = 0.0f;
            numPressedButtons -= 1;
        }
    }

    bool IsValid(int index) {
        return index >= 0 && index <= 24;
    }

    // Index goes first row 0 -> 4, second row 5 -> 9 etc.
    void ButtonPress(int index) {
        if (gameWon) {
            return;
        }

        if (!isPressed && Input(Trigger, rightController)) {
            isPressed = true;

            int left = index - 1;
            if (index % 5 != 0 && IsValid(left)) {
                Toggle(left);
            }

            int right = index + 1;
            if (index % 5 != 4 && IsValid(right)) {
                Toggle(right);
            }

            int up = index - 5;
            if (index > 4 && IsValid(up)) {
                Toggle(up);
            }

            int down = index + 5;
            if (index < 20 && IsValid(down)) {
                Toggle(down);
            }

            Toggle(index);

            if (numPressedButtons == 25) {
                gameWon = true;
                //SendMessage(somewhere);
                print("Won the game of lights out.\n");
            }
        }
    }

    void b_0_0() {
        ButtonPress(0);
    }

    void b_0_1() {
        ButtonPress(1);
    }

    void b_0_2() {
        ButtonPress(2);
    }

    void b_0_3() {
        ButtonPress(3);
    }

    void b_0_4() {
        ButtonPress(4);
    }

    void b_1_0() {
        ButtonPress(5);
    }

    void b_1_1() {
        ButtonPress(6);
    }

    void b_1_2() {
        ButtonPress(7);
    }

    void b_1_3() {
        ButtonPress(8);
    }

    void b_1_4() {
        ButtonPress(9);
    }

    void b_2_0() {
        ButtonPress(10);
    }

    void b_2_1() {
        ButtonPress(11);
    }

    void b_2_2() {
        ButtonPress(12);
    }

    void b_2_3() {
        ButtonPress(13);
    }

    void b_2_4() {
        ButtonPress(14);
    }

    void b_3_0() {
        ButtonPress(15);
    }

    void b_3_1() {
        ButtonPress(16);
    }

    void b_3_2() {
        ButtonPress(17);
    }

    void b_3_3() {
        ButtonPress(18);
    }

    void b_3_4() {
        ButtonPress(19);
    }

    void b_4_0() {
        ButtonPress(20);
    }

    void b_4_1() {
        ButtonPress(21);
    }

    void b_4_2() {
        ButtonPress(22);
    }

    void b_4_3() {
        ButtonPress(23);
    }

    void b_4_4() {
        ButtonPress(24);
    }
}

Remove unused variables in the lights out script.


#include "../../assets/scripts/entities/light_cube.as"

void move_forward_begin() {
	auto player = get_entity(player_id);
	auto player_state = player.get_state_component();
	if (player_state.current_state() != "run") {
		player_state.change_state("run");
	}
	player.get_character_physics_component().velocity(3.f);
}

void move_forward_end() {
	auto player = get_entity(player_id);
	auto player_state = player.get_state_component();
	if (player_state.current_state() == "run") {
		player_state.change_state("stand");
		player.get_character_physics_component().velocity(0.f);
	}
}

void move_backward_begin() {
	auto player = get_entity(player_id);
	auto player_state = player.get_state_component();
	if (player_state.current_state() != "walk_backward") {
		player_state.change_state("walk_backward");
	}
	player.get_character_physics_component().velocity(-1.f);
}

void move_backward_end() {
	auto player = get_entity(player_id);
	auto player_state = player.get_state_component();
	if (player_state.current_state() == "walk_backward") {
		player_state.change_state("stand");
		player.get_character_physics_component().velocity(0.f);
	}
}

void move_right_begin() {
	auto player = get_entity(player_id);
	player.get_character_physics_component().angular_velocity(-1.0f);
	if (player.get_state_component().current_state() == "stand") {
		if (!player.get_animation_component().is_playing("turn_right")) {
			player.get_animation_component().play_ani("turn_right");
		}
	}
}

void move_right_end() {
	auto player = get_entity(player_id);
	player.get_character_physics_component().angular_velocity(0.0f);
	if (player.get_state_component().current_state() == "stand") {
		player.get_animation_component().play_ani("stand");
	}
}

void move_left_begin() {
	auto player = get_entity(player_id);
	player.get_character_physics_component().angular_velocity(1.0f);
	if (player.get_state_component().current_state() == "stand") {
		if (!player.get_animation_component().is_playing("turn_right")) {
			player.get_animation_component().play_ani("turn_right");
		}
	}
}

void move_left_end() {
	auto player = get_entity(player_id);
	player.get_character_physics_component().angular_velocity(0.0f);
	if (player.get_state_component().current_state() == "stand") {
		player.get_animation_component().play_ani("stand");
	}
}

void stand_begin() {
	auto player = get_entity(player_id);
	auto player_state = player.get_state_component();
	if (player_state.current_state() != "stand") {
		player_state.change_state("stand");
	}
}

void throw_light_begin() {
	auto player = get_entity(player_id);
	glm::vec3 direction = player.get_character_physics_component().direction();
	glm::vec3 position = player.position();
	light_cube lc(position + direction * 2.f, player.rotation());
	lc.impl().get_physics_component().apply_central_impulse((direction + glm::vec3(0.f, 0.2f, 0.f)) * 5.f);
}

void throw_light_end() {

}

add input callback for throwing light cubes with spaces


#!/bin/bash -e
# ========================================================================
# Copyright (c) 2015-2017 T. R. Burghart.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ========================================================================
#
# Build and install/replace an OTP instance from source.
#

readonly  sdir="$(cd "$(dirname "$0")" && pwd)"
readonly  sname="${0##*/}"
readonly  spath="$sdir/$sname"

readonly  makejobs='5'
readonly  verbosity="${V:-0}"

usage()
{
    echo "Usage: $sname [-h{0|1|2}] otp-name-or-path otp-inst-label" >&2
    exit 1
}

case "$1" in
    '-h0' )
        hipe_modes='false'
        shift
        ;;
    '-h1' )
        hipe_modes='true'
        shift
        ;;
    '-h2' )
        hipe_modes='false true'
        shift
        ;;
    * )
        unset hipe_modes
        ;;
esac

[[ $# -eq 2 ]] || usage
[[ "$2" != */* ]] || usage

readonly  otp_name="${2}"

case "$1" in
    */* )
        otp_src_dir="$1"
        ;;
    . )
        otp_src_dir="$(pwd)"
        ;;
    .. )
        otp_src_dir="$(dirname "$(pwd)")"
        ;;
    * )
        if [[ -d "$1" ]]
        then
            otp_src_dir="$1"
        else
            otp_src_dir="$BASHO_PRJ_BASE/$1"
        fi
        ;;
esac

kerl_deactivate 2>/dev/null || true
reset_lenv 2>/dev/null || true

cd "$otp_src_dir"
readonly  otp_src_dir="$(pwd)"

unset   ERL_TOP ERL_LIBS MAKEFLAGS V
unset   EXCLUDE_OSX_RT_VERS_FLAG

for n in \
    os.type \
    otp.install.base \
    otp.source.base \
    otp.source.version \
    env.tool.defaults \
    otp.config.opts
do
    . "$LOCAL_ENV_DIR/$n" || exit $?
done

if [[ -n "$hipe_modes" ]]
then
    if [[ " $hipe_modes " == *\ true\ * ]] && ! $otp_hipe_supported
    then
        echo HiPE is not supported on this Release/Platform >&2
        exit 1
    fi
elif $otp_hipe_supported
then
    hipe_modes='false true'
else
    hipe_modes='false'
fi

ERL_TOP="$(pwd)"
[[ "$PATH" == "$ERL_TOP/bin":* ]] || PATH="$ERL_TOP/bin:$PATH"
export  ERL_TOP PATH

case " $otp_config_opts " in
    *\ --with-odbc[\ =]*)
        ;;
    *\ --without-odbc\ *)
        ;;
    *)
        otp_config_opts+=' --without-odbc'
        ;;
esac

for hipe in $hipe_modes
do
    if $hipe
    then
        otp_label="${otp_name}h"
        hipe_flag='--enable-hipe'
    else
        otp_label="$otp_name"
        hipe_flag='--disable-hipe'
    fi
    otp_dest="$otp_install_base/$otp_label"
    build_cfg="--prefix $otp_dest $otp_config_opts $hipe_flag"
    build_log="build.$os_type.$otp_label.txt"
    docs_log="docs.$os_type.$otp_label.txt"
    install_log="install.$os_type.$otp_label.txt"

    $GIT clean -fdqx -e /env -e /.idea/ -e '*.iml' -e '/*.txt' \
    || $GIT clean -f -f -dqx -e /env -e /.idea/ -e '*.iml' -e '/*.txt' \
    || true

    [[ $makejobs -lt 2 ]] || export MAKEFLAGS="-j$makejobs"
    [[ $verbosity -lt 1 ]] || export V="$verbosity"

    printf 'commit:\t' >"$build_log"
    $GIT show-ref --heads --head --hash | head -1 >>"$build_log"
    /bin/date >>"$build_log"
    env | sort >>"$build_log"
    echo "./otp_build setup -a $build_cfg" | tee -a "$build_log"
    ./otp_build setup -a $build_cfg 1>>"$build_log" 2>&1
    /bin/date >>"$build_log"

    unset   V MAKEFLAGS

    if [[ -d "$otp_dest" ]]
    then
        /bin/rm -rf "$otp_dest/bin" "$otp_dest/lib" "$otp_dest/index.html"
    else
        /bin/mkdir -m 2775 "$otp_dest"
    fi

    /bin/date >"$install_log"
    echo "$MAKE install" | tee -a "$install_log"
    $MAKE install 1>>"$install_log" 2>&1
    /bin/date >>"$install_log"

    $ECP "$build_log" "$install_log" "$otp_dest"
    [[ -e "$otp_dest/activate" ]] || \
        ln -s "$LOCAL_ENV_DIR/otp.activate" "$otp_dest/activate"

    if [[ "$otp_install_base/otp-$otp_src_vsn_major" -ef "$otp_dest" ]]
    then
        # run this before installing QuickCheck - no debug info in EQC beams
        echo "otp.gen.plt $otp_label" | tee -a "$install_log"
        otp.gen.plt "$otp_label" 1>>"$install_log" 2>&1

        echo "/opt/QuickCheck/install_qc $otp_dest" | tee -a "$install_log"
        if ! /opt/QuickCheck/install_qc "$otp_dest" 1>>"$install_log" 2>&1
        then
            echo 'Failed to install QuickCheck into' "$otp_dest" >&2
        fi
        /bin/date >>"$install_log"
        $ECP "$install_log" "$otp_dest"

        /bin/date >"$docs_log"
        echo "$MAKE docs install-docs" | tee -a "$docs_log"
        $MAKE docs install-docs 1>>"$docs_log" 2>&1
        echo "otp.gen.doc.index $otp_label" | tee -a "$docs_log"
        otp.gen.doc.index "$otp_label" >>"$docs_log"
        /bin/date >>"$docs_log"
        $ECP $docs_log "$otp_dest"
    fi
done

#!/bin/bash -e
# ========================================================================
# Copyright (c) 2015-2017 T. R. Burghart.
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ========================================================================
#
# Build and install/replace an OTP instance from source.
#

readonly  sdir="$(cd "$(dirname "$0")" && pwd)"
readonly  sname="${0##*/}"
readonly  spath="$sdir/$sname"

readonly  makejobs='5'
readonly  verbosity="${V:-0}"

usage()
{
    echo "Usage: $sname [-h{0|1|2}] otp-name-or-path otp-inst-label" >&2
    exit 1
}

case "$1" in
    '-h0' )
        hipe_modes='false'
        shift
        ;;
    '-h1' )
        hipe_modes='true'
        shift
        ;;
    '-h2' )
        hipe_modes='false true'
        shift
        ;;
    * )
        unset hipe_modes
        ;;
esac

[[ $# -eq 2 ]] || usage
[[ "$2" != */* ]] || usage

readonly  otp_name="${2}"

case "$1" in
    */* )
        otp_src_dir="$1"
        ;;
    . )
        otp_src_dir="$(pwd)"
        ;;
    .. )
        otp_src_dir="$(dirname "$(pwd)")"
        ;;
    * )
        if [[ -d "$1" ]]
        then
            otp_src_dir="$1"
        else
            otp_src_dir="$BASHO_PRJ_BASE/$1"
        fi
        ;;
esac

kerl_deactivate 2>/dev/null || true
reset_lenv 2>/dev/null || true

cd "$otp_src_dir"
readonly  otp_src_dir="$(pwd)"

unset   ERL_TOP ERL_LIBS MAKEFLAGS V
unset   EXCLUDE_OSX_RT_VERS_FLAG

for n in \
    os.type \
    otp.install.base \
    otp.source.base \
    otp.source.version \
    env.tool.defaults \
    otp.config.opts
do
    . "$LOCAL_ENV_DIR/$n" || exit $?
done

if [[ -n "$hipe_modes" ]]
then
    if [[ " $hipe_modes " == *\ true\ * ]] && ! $otp_hipe_supported
    then
        echo HiPE is not supported on this Release/Platform >&2
        exit 1
    fi
elif $otp_hipe_supported
then
    hipe_modes='false true'
else
    hipe_modes='false'
fi

ERL_TOP="$(pwd)"
[[ "$PATH" == "$ERL_TOP/bin":* ]] || PATH="$ERL_TOP/bin:$PATH"
export  ERL_TOP PATH

case " $otp_config_opts " in
    *\ --with-odbc[\ =]*)
        ;;
    *\ --without-odbc\ *)
        ;;
    *)
        otp_config_opts+=' --without-odbc'
        ;;
esac

for hipe in $hipe_modes
do
    if $hipe
    then
        otp_label="${otp_name}h"
        hipe_flag='--enable-hipe'
    else
        otp_label="$otp_name"
        hipe_flag='--disable-hipe'
    fi
    otp_dest="$otp_install_base/$otp_label"
    build_cfg="--prefix $otp_dest $otp_config_opts $hipe_flag"
    build_log="build.$os_type.$otp_label.txt"
    docs_log="docs.$os_type.$otp_label.txt"
    install_log="install.$os_type.$otp_label.txt"

    $GIT clean -fdqx -e /env -e /.idea/ -e '*.iml' -e '/*.txt' \
    || $GIT clean -f -f -dqx -e /env -e /.idea/ -e '*.iml' -e '/*.txt' \
    || true

    [[ $makejobs -lt 2 ]] || export MAKEFLAGS="-j$makejobs"
    [[ $verbosity -lt 1 ]] || export V="$verbosity"

    printf 'commit:\t' >"$build_log"
    $GIT show-ref --heads --head --hash | head -1 >>"$build_log"
    /bin/date >>"$build_log"
    env | sort >>"$build_log"
    echo "./otp_build setup -a $build_cfg" | tee -a "$build_log"
    ./otp_build setup -a $build_cfg 1>>"$build_log" 2>&1
    /bin/date >>"$build_log"

    unset   V MAKEFLAGS

    if [[ -d "$otp_dest" ]]
    then
        /bin/rm -rf "$otp_dest/bin" "$otp_dest/lib" "$otp_dest/index.html"
    else
        /bin/mkdir -m 2775 "$otp_dest"
    fi

    /bin/date >"$install_log"
    echo "$MAKE install" | tee -a "$install_log"
    $MAKE install 1>>"$install_log" 2>&1
    if [[ $otp_src_vsn_major -ge 20 && ! -f "$otp_dest/bin/rebar3" ]]
    then
        echo "Installing $otp_dest/bin/rebar3" >>"$install_log"
        wget -O "$otp_dest/bin/rebar3" 'https://s3.amazonaws.com/rebar3/rebar3'
        /bin/chmod +x "$otp_dest/bin/rebar3"
    fi
    /bin/date >>"$install_log"

    $ECP "$build_log" "$install_log" "$otp_dest"
    [[ -e "$otp_dest/activate" ]] || \
        ln -s "$LOCAL_ENV_DIR/otp.activate" "$otp_dest/activate"

    if [[ "$otp_install_base/otp-$otp_src_vsn_major" -ef "$otp_dest" ]]
    then
        # run this before installing QuickCheck - no debug info in EQC beams
        echo "otp.gen.plt $otp_label" | tee -a "$install_log"
        otp.gen.plt "$otp_label" 1>>"$install_log" 2>&1

        echo "/opt/QuickCheck/install_qc $otp_dest" | tee -a "$install_log"
        if ! /opt/QuickCheck/install_qc "$otp_dest" 1>>"$install_log" 2>&1
        then
            echo 'Failed to install QuickCheck into' "$otp_dest" >&2
        fi
        /bin/date >>"$install_log"
        $ECP "$install_log" "$otp_dest"

        /bin/date >"$docs_log"
        echo "$MAKE docs install-docs" | tee -a "$docs_log"
        $MAKE docs install-docs 1>>"$docs_log" 2>&1
        echo "otp.gen.doc.index $otp_label" | tee -a "$docs_log"
        otp.gen.doc.index "$otp_label" >>"$docs_log"
        /bin/date >>"$docs_log"
        $ECP $docs_log "$otp_dest"
    fi
done

install rebar3 to OTP-20+ bin directory

install rebar3 to OTP-20+ bin directory


//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flash.utils.ByteArray;

import flump.executor.Executor;
import flump.executor.Future;

import starling.core.Starling;

/**
 * Loads zip files created by the flump exporter and parses them into Library instances.
 */
public class LibraryLoader
{
    /**
     * Loads a Library from the zip in the given bytes.
     *
     * @param bytes The bytes containing the zip
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources out of the
     * bytes. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadBytes (bytes :ByteArray, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(bytes, scaleFactor).load);
    }

    /**
     * Loads a Library from the zip at the given url.
     *
     * @param bytes The url where the zip can be found
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources from the
     * url. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadURL (url :String, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(url, scaleFactor).load);
    }

    /** @private */
    public static const LIBRARY_LOCATION :String = "library.json";
    /** @private */
    public static const MD5_LOCATION :String = "md5";
    /** @private */
    public static const VERSION_LOCATION :String = "version";

    /**
     * @private
     * The version produced and parsable by this version of the code. The version in a resources
     * zip must equal the version compiled into the parsing code for parsing to succeed.
     */
    public static const VERSION :String = "2";
}

}

import deng.fzip.FZip;
import deng.fzip.FZipErrorEvent;
import deng.fzip.FZipEvent;
import deng.fzip.FZipFile;

import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Dictionary;

import flump.display.Library;
import flump.display.LibraryLoader;
import flump.display.Movie;
import flump.executor.Executor;
import flump.executor.Future;
import flump.executor.FutureTask;
import flump.executor.load.ImageLoader;
import flump.executor.load.LoadedImage;
import flump.mold.AtlasMold;
import flump.mold.AtlasTextureMold;
import flump.mold.LibraryMold;
import flump.mold.MovieMold;
import flump.mold.TextureGroupMold;

import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.Image;
import starling.textures.Texture;

interface SymbolCreator
{
    function create (library :Library) :DisplayObject;
}

class LibraryImpl
    implements Library
{
    public function LibraryImpl (baseTextures :Vector.<Texture>, creators :Dictionary) {
        _baseTextures = baseTextures;
        _creators = creators;
    }

    public function createMovie (symbol :String) :Movie {
        return Movie(createDisplayObject(symbol));
    }

    public function createImage (symbol :String) :Image {
        const disp :DisplayObject = createDisplayObject(symbol);
        if (disp is Movie) throw new Error(symbol + " is not an Image");
        return Image(disp);
    }

    public function getImageTexture (symbol :String) :Texture {
        checkNotDisposed();
        var creator :SymbolCreator = requireSymbolCreator(symbol);
        if (!(creator is ImageCreator)) throw new Error(symbol + " is not an Image");
        return ImageCreator(creator).texture;
    }

    public function get movieSymbols () :Vector.<String> {
        checkNotDisposed();
        const names :Vector.<String> = new <String>[];
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is MovieCreator) names.push(creatorName);
        }
        return names;
    }

    public function get imageSymbols () :Vector.<String> {
        checkNotDisposed();
        const names :Vector.<String> = new <String>[];
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is ImageCreator) names.push(creatorName);
        }
        return names;
    }

    public function createDisplayObject (name :String) :DisplayObject {
        checkNotDisposed();
        return requireSymbolCreator(name).create(this);
    }

    public function dispose () :void {
        checkNotDisposed();
        for each (var tex :Texture in _baseTextures) {
            tex.dispose();
        }
        _baseTextures = null;
        _creators = null;
    }

    protected function requireSymbolCreator (name :String) :SymbolCreator {
        var creator :SymbolCreator = _creators[name];
        if (creator == null) throw new Error("No such id '" + name + "'");
        return creator;
    }

    protected function checkNotDisposed () :void {
        if (_baseTextures == null) {
            throw new Error("This Library has been disposed");
        }
    }

    protected var _creators :Dictionary;
    protected var _baseTextures :Vector.<Texture>;
}

class Loader
{
    public function Loader (toLoad :Object, scaleFactor :Number) {
        _scaleFactor = (scaleFactor > 0 ? scaleFactor : Starling.contentScaleFactor);
        _toLoad = toLoad;
    }

    public function load (future :FutureTask) :void {
        _future = future;

        _zip.addEventListener(Event.COMPLETE, _future.monitoredCallback(onZipLoadingComplete));
        _zip.addEventListener(FZipErrorEvent.PARSE_ERROR, _future.fail);
        _zip.addEventListener(FZipEvent.FILE_LOADED, _future.monitoredCallback(onFileLoaded));

        if (_toLoad is String) _zip.load(new URLRequest(String(_toLoad)));
        else _zip.loadBytes(ByteArray(_toLoad));
    }

    protected function onFileLoaded (e :FZipEvent) :void {
        const loaded :FZipFile = _zip.removeFileAt(_zip.getFileCount() - 1);
        const name :String = loaded.filename;
        if (name == LibraryLoader.LIBRARY_LOCATION) {
            const jsonString :String = loaded.content.readUTFBytes(loaded.content.length);
            _lib = LibraryMold.fromJSON(JSON.parse(jsonString));
        } else if (name.indexOf(PNG, name.length - PNG.length) != -1) {
            _pngBytes[name] = loaded.content;
        } else if (name == LibraryLoader.VERSION_LOCATION) {
            const zipVersion :String = loaded.content.readUTFBytes(loaded.content.length)
            if (zipVersion != LibraryLoader.VERSION) {
                throw new Error("Zip is version " + zipVersion + " but the code needs " + LibraryLoader.VERSION);
            }
            _versionChecked = true;
        } else if (name == LibraryLoader.MD5_LOCATION ) { // Nothing to verify
        } else {} // ignore unknown files
    }

    protected function onZipLoadingComplete (..._) :void {
        _zip = null;
        if (_lib == null) throw new Error(LibraryLoader.LIBRARY_LOCATION + " missing from zip");
        if (!_versionChecked) throw new Error(LibraryLoader.VERSION_LOCATION + " missing from zip");
        const loader :ImageLoader = new ImageLoader();
        _pngLoaders.terminated.add(_future.monitoredCallback(onPngLoadingComplete));

        // Determine the scale factor we want to use
        var textureGroup :TextureGroupMold = _lib.bestTextureGroupForScaleFactor(_scaleFactor);
        if (textureGroup != null) {
            for each (var atlas :AtlasMold in textureGroup.atlases) {
                loadAtlas(loader, atlas);
            }
        }
        _pngLoaders.shutdown();
    }

    protected function loadAtlas (loader :ImageLoader, atlas :AtlasMold) :void {
        const pngBytes :* = _pngBytes[atlas.file];
        if (pngBytes === undefined) {
            throw new Error("Expected an atlas '" + atlas.file + "', but it wasn't in the zip");
        }
        const atlasFuture :Future = loader.loadFromBytes(pngBytes, _pngLoaders);
        atlasFuture.failed.add(onPngLoadingFailed);
        atlasFuture.succeeded.add(function (img :LoadedImage) :void {
            var scale :Number = atlas.scaleFactor;
            const baseTexture :Texture = Texture.fromBitmapData(
                img.bitmapData,
                false,   // generateMipMaps
                false,  // optimizeForRenderToTexture
                scale);

            _baseTextures.push(baseTexture);

            if (!Starling.handleLostContext) {
                img.bitmapData.dispose();
            }

            for each (var atlasTexture :AtlasTextureMold in atlas.textures) {
                var bounds :Rectangle = atlasTexture.bounds;
                var offset :Point = atlasTexture.origin;

                // Starling expects subtexture bounds to be unscaled
                if (scale != 1) {
                    bounds = bounds.clone();
                    bounds.x /= scale;
                    bounds.y /= scale;
                    bounds.width /= scale;
                    bounds.height /= scale;

                    offset = offset.clone();
                    offset.x /= scale;
                    offset.y /= scale;
                }

                _creators[atlasTexture.symbol] = new ImageCreator(
                    Texture.fromTexture(baseTexture, bounds),
                    offset,
                    atlasTexture.symbol);
            }
        });
    }

    protected function onPngLoadingComplete (..._) :void {
        for each (var movie :MovieMold in _lib.movies) {
            movie.fillLabels();
            _creators[movie.id] = new MovieCreator(movie, _lib.frameRate);
        }
        _future.succeed(new LibraryImpl(_baseTextures, _creators));
    }

    protected function onPngLoadingFailed (e :*) :void {
        if (_future.isComplete) return;
        _future.fail(e);
        _pngLoaders.shutdownNow();
    }

    protected var _toLoad :Object;
    protected var _scaleFactor :Number;
    protected var _future :FutureTask;
    protected var _versionChecked :Boolean;

    protected var _zip :FZip = new FZip();
    protected var _lib :LibraryMold;

    protected const _baseTextures :Vector.<Texture> = new <Texture>[];
    protected const _creators :Dictionary = new Dictionary();//<name, ImageCreator/MovieCreator>
    protected const _pngBytes :Dictionary = new Dictionary();//<String name, ByteArray>
    protected const _pngLoaders :Executor = new Executor(1);

    protected static const PNG :String = ".png";
}

class ImageCreator
    implements SymbolCreator
{
    public var texture :Texture;
    public var origin :Point;
    public var symbol :String;

    public function ImageCreator (texture :Texture, origin :Point, symbol :String) {
        this.texture = texture;
        this.origin = origin;
        this.symbol = symbol;
    }

    public function create (library :Library) :DisplayObject {
        const image :Image = new Image(texture);
        image.pivotX = origin.x;
        image.pivotY = origin.y;
        image.name = symbol;
        return image;
    }
}

class MovieCreator
    implements SymbolCreator
{
    public var mold :MovieMold;
    public var frameRate :Number;

    public function MovieCreator (mold :MovieMold, frameRate :Number) {
        this.mold = mold;
        this.frameRate = frameRate;
    }

    public function create (library :Library) :DisplayObject {
        return new Movie(mold, frameRate, library);
    }
}

//
// Flump - Copyright 2013 Flump Authors

package flump.display {

import flash.utils.ByteArray;

import flump.executor.Executor;
import flump.executor.Future;

import starling.core.Starling;

/**
 * Loads zip files created by the flump exporter and parses them into Library instances.
 */
public class LibraryLoader
{
    /**
     * Loads a Library from the zip in the given bytes.
     *
     * @param bytes The bytes containing the zip
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources out of the
     * bytes. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadBytes (bytes :ByteArray, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(bytes, scaleFactor).load);
    }

    /**
     * Loads a Library from the zip at the given url.
     *
     * @param bytes The url where the zip can be found
     *
     * @param executor The executor on which the loading should run. If not specified, it'll run on
     * a new single-use executor.
     *
     * @param scaleFactor the desired scale factor of the textures to load. If the Library contains
     * textures with multiple scale factors, loader will load the textures with the scale factor
     * closest to this value. If scaleFactor <= 0 (the default), Starling.contentScaleFactor will be
     * used.
     *
     * @return a Future to use to track the success or failure of loading the resources from the
     * url. If the loading succeeds, the Future's onSuccess will fire with an instance of
     * Library. If it fails, the Future's onFail will fire with the Error that caused the
     * loading failure.
     */
    public static function loadURL (url :String, executor :Executor=null, scaleFactor :Number=-1) :Future {
        return (executor || new Executor(1)).submit(new Loader(url, scaleFactor).load);
    }

    /** @private */
    public static const LIBRARY_LOCATION :String = "library.json";
    /** @private */
    public static const MD5_LOCATION :String = "md5";
    /** @private */
    public static const VERSION_LOCATION :String = "version";

    /**
     * @private
     * The version produced and parsable by this version of the code. The version in a resources
     * zip must equal the version compiled into the parsing code for parsing to succeed.
     */
    public static const VERSION :String = "2";
}

}

import deng.fzip.FZip;
import deng.fzip.FZipErrorEvent;
import deng.fzip.FZipEvent;
import deng.fzip.FZipFile;

import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Dictionary;

import flump.display.Library;
import flump.display.LibraryLoader;
import flump.display.Movie;
import flump.executor.Executor;
import flump.executor.Future;
import flump.executor.FutureTask;
import flump.executor.load.ImageLoader;
import flump.executor.load.LoadedImage;
import flump.mold.AtlasMold;
import flump.mold.AtlasTextureMold;
import flump.mold.LibraryMold;
import flump.mold.MovieMold;
import flump.mold.TextureGroupMold;

import starling.core.Starling;
import starling.display.DisplayObject;
import starling.display.Image;
import starling.textures.Texture;

interface SymbolCreator
{
    function create (library :Library) :DisplayObject;
}

class LibraryImpl
    implements Library
{
    public function LibraryImpl (baseTextures :Vector.<Texture>, creators :Dictionary) {
        _baseTextures = baseTextures;
        _creators = creators;
    }

    public function createMovie (symbol :String) :Movie {
        return Movie(createDisplayObject(symbol));
    }

    public function createImage (symbol :String) :Image {
        const disp :DisplayObject = createDisplayObject(symbol);
        if (disp is Movie) throw new Error(symbol + " is not an Image");
        return Image(disp);
    }

    public function getImageTexture (symbol :String) :Texture {
        checkNotDisposed();
        var creator :SymbolCreator = requireSymbolCreator(symbol);
        if (!(creator is ImageCreator)) throw new Error(symbol + " is not an Image");
        return ImageCreator(creator).texture;
    }

    public function get movieSymbols () :Vector.<String> {
        checkNotDisposed();
        const names :Vector.<String> = new <String>[];
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is MovieCreator) names.push(creatorName);
        }
        return names;
    }

    public function get imageSymbols () :Vector.<String> {
        checkNotDisposed();
        const names :Vector.<String> = new <String>[];
        for (var creatorName :String in _creators) {
            if (_creators[creatorName] is ImageCreator) names.push(creatorName);
        }
        return names;
    }

    public function createDisplayObject (name :String) :DisplayObject {
        checkNotDisposed();
        return requireSymbolCreator(name).create(this);
    }

    public function dispose () :void {
        checkNotDisposed();
        for each (var tex :Texture in _baseTextures) {
            tex.dispose();
        }
        _baseTextures = null;
        _creators = null;
    }

    protected function requireSymbolCreator (name :String) :SymbolCreator {
        var creator :SymbolCreator = _creators[name];
        if (creator == null) throw new Error("No such id '" + name + "'");
        return creator;
    }

    protected function checkNotDisposed () :void {
        if (_baseTextures == null) {
            throw new Error("This Library has been disposed");
        }
    }

    protected var _creators :Dictionary;
    protected var _baseTextures :Vector.<Texture>;
}

class Loader
{
    public function Loader (toLoad :Object, scaleFactor :Number) {
        _scaleFactor = (scaleFactor > 0 ? scaleFactor : Starling.contentScaleFactor);
        _toLoad = toLoad;
    }

    public function load (future :FutureTask) :void {
        _future = future;

        _zip.addEventListener(Event.COMPLETE, _future.monitoredCallback(onZipLoadingComplete));
        _zip.addEventListener(IOErrorEvent.IO_ERROR, _future.fail);
        _zip.addEventListener(FZipErrorEvent.PARSE_ERROR, _future.fail);
        _zip.addEventListener(FZipEvent.FILE_LOADED, _future.monitoredCallback(onFileLoaded));

        if (_toLoad is String) _zip.load(new URLRequest(String(_toLoad)));
        else _zip.loadBytes(ByteArray(_toLoad));
    }

    protected function onFileLoaded (e :FZipEvent) :void {
        const loaded :FZipFile = _zip.removeFileAt(_zip.getFileCount() - 1);
        const name :String = loaded.filename;
        if (name == LibraryLoader.LIBRARY_LOCATION) {
            const jsonString :String = loaded.content.readUTFBytes(loaded.content.length);
            _lib = LibraryMold.fromJSON(JSON.parse(jsonString));
        } else if (name.indexOf(PNG, name.length - PNG.length) != -1) {
            _pngBytes[name] = loaded.content;
        } else if (name == LibraryLoader.VERSION_LOCATION) {
            const zipVersion :String = loaded.content.readUTFBytes(loaded.content.length)
            if (zipVersion != LibraryLoader.VERSION) {
                throw new Error("Zip is version " + zipVersion + " but the code needs " + LibraryLoader.VERSION);
            }
            _versionChecked = true;
        } else if (name == LibraryLoader.MD5_LOCATION ) { // Nothing to verify
        } else {} // ignore unknown files
    }

    protected function onZipLoadingComplete (..._) :void {
        _zip = null;
        if (_lib == null) throw new Error(LibraryLoader.LIBRARY_LOCATION + " missing from zip");
        if (!_versionChecked) throw new Error(LibraryLoader.VERSION_LOCATION + " missing from zip");
        const loader :ImageLoader = new ImageLoader();
        _pngLoaders.terminated.add(_future.monitoredCallback(onPngLoadingComplete));

        // Determine the scale factor we want to use
        var textureGroup :TextureGroupMold = _lib.bestTextureGroupForScaleFactor(_scaleFactor);
        if (textureGroup != null) {
            for each (var atlas :AtlasMold in textureGroup.atlases) {
                loadAtlas(loader, atlas);
            }
        }
        _pngLoaders.shutdown();
    }

    protected function loadAtlas (loader :ImageLoader, atlas :AtlasMold) :void {
        const pngBytes :* = _pngBytes[atlas.file];
        if (pngBytes === undefined) {
            throw new Error("Expected an atlas '" + atlas.file + "', but it wasn't in the zip");
        }
        const atlasFuture :Future = loader.loadFromBytes(pngBytes, _pngLoaders);
        atlasFuture.failed.add(onPngLoadingFailed);
        atlasFuture.succeeded.add(function (img :LoadedImage) :void {
            var scale :Number = atlas.scaleFactor;
            const baseTexture :Texture = Texture.fromBitmapData(
                img.bitmapData,
                false,   // generateMipMaps
                false,  // optimizeForRenderToTexture
                scale);

            _baseTextures.push(baseTexture);

            if (!Starling.handleLostContext) {
                img.bitmapData.dispose();
            }

            for each (var atlasTexture :AtlasTextureMold in atlas.textures) {
                var bounds :Rectangle = atlasTexture.bounds;
                var offset :Point = atlasTexture.origin;

                // Starling expects subtexture bounds to be unscaled
                if (scale != 1) {
                    bounds = bounds.clone();
                    bounds.x /= scale;
                    bounds.y /= scale;
                    bounds.width /= scale;
                    bounds.height /= scale;

                    offset = offset.clone();
                    offset.x /= scale;
                    offset.y /= scale;
                }

                _creators[atlasTexture.symbol] = new ImageCreator(
                    Texture.fromTexture(baseTexture, bounds),
                    offset,
                    atlasTexture.symbol);
            }
        });
    }

    protected function onPngLoadingComplete (..._) :void {
        for each (var movie :MovieMold in _lib.movies) {
            movie.fillLabels();
            _creators[movie.id] = new MovieCreator(movie, _lib.frameRate);
        }
        _future.succeed(new LibraryImpl(_baseTextures, _creators));
    }

    protected function onPngLoadingFailed (e :*) :void {
        if (_future.isComplete) return;
        _future.fail(e);
        _pngLoaders.shutdownNow();
    }

    protected var _toLoad :Object;
    protected var _scaleFactor :Number;
    protected var _future :FutureTask;
    protected var _versionChecked :Boolean;

    protected var _zip :FZip = new FZip();
    protected var _lib :LibraryMold;

    protected const _baseTextures :Vector.<Texture> = new <Texture>[];
    protected const _creators :Dictionary = new Dictionary();//<name, ImageCreator/MovieCreator>
    protected const _pngBytes :Dictionary = new Dictionary();//<String name, ByteArray>
    protected const _pngLoaders :Executor = new Executor(1);

    protected static const PNG :String = ".png";
}

class ImageCreator
    implements SymbolCreator
{
    public var texture :Texture;
    public var origin :Point;
    public var symbol :String;

    public function ImageCreator (texture :Texture, origin :Point, symbol :String) {
        this.texture = texture;
        this.origin = origin;
        this.symbol = symbol;
    }

    public function create (library :Library) :DisplayObject {
        const image :Image = new Image(texture);
        image.pivotX = origin.x;
        image.pivotY = origin.y;
        image.name = symbol;
        return image;
    }
}

class MovieCreator
    implements SymbolCreator
{
    public var mold :MovieMold;
    public var frameRate :Number;

    public function MovieCreator (mold :MovieMold, frameRate :Number) {
        this.mold = mold;
        this.frameRate = frameRate;
    }

    public function create (library :Library) :DisplayObject {
        return new Movie(mold, frameRate, library);
    }
}

handle IO error failure

LibraryLoader: handle IO error failure


package lambmei.starling.display
{
	import flash.display.MovieClip;
	import flash.geom.Point;
	import flash.system.System;

	import starling.display.Button;
	import starling.display.DisplayObject;
	import starling.display.DisplayObjectContainer;
	import starling.display.Shape;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.textures.Texture;
	import starling.utils.Line;

	public class HandleSheet extends Sprite
	{

		/** 點擊 將物件移動到前方 **/
		protected var _touchBringToFront:Boolean
		public function get touchBringToFront():Boolean { return _touchBringToFront;}
		public function set touchBringToFront(value:Boolean):void{ _touchBringToFront = value; }

		/** 是否選擇 **/
		protected var _selected:Boolean
		public function get selected():Boolean { return _selected;}
		public function set selected(value:Boolean):void
		{
			if(value!=_selected){
				_selected = value;
				_selectedGroup.visible = _selected
			}
		}

		/** 是否有使用 控制按鈕 Read Only **/
		protected var _useCtrlButton:Boolean
		public function get useCtrlButton():Boolean
		{
			return _useCtrlButton;
		}

//		public function set useCtrlButton(value:Boolean):void
//		{
//			_useCtrlButton = value;
//		}

		//Const Vars
		protected static const CTRL_BUTTON_NAME:String = "HandlectrlBtn"
		public static const ALIGN_CENTER:String = "center"
		public static const ALIGN_LT:String = "LT"

		//UI
		protected var _contents:DisplayObject
		protected var _ctrlButton:DisplayObject
		protected var _selectedGroup:Sprite
		protected var _shape:Shape

		public function HandleSheet(contents:DisplayObject=null)
		{
			addEventListener(TouchEvent.TOUCH, onTouch);
			useHandCursor = true;

			_touchBringToFront = true

			if (contents)
			{
				var _w:Number = contents.width
				var _h:Number = contents.height
				var _halfW:Number = _w/2
				var _halfH:Number= _h/2
				//將物件置中
				contents.x = int(_halfW * -1);
				contents.y = int(_halfH * -1);
				addChild(contents);
				_contents = contents

			}
			//init SelectGroup
			initSelectedGroup()
		}

		/** 初始化選擇的群組**/
		protected function initSelectedGroup():void
		{

			_selectedGroup = new Sprite()
			this.addChild(this._selectedGroup);

			_shape = new Shape()
			updateLine()
			_selectedGroup.addChild(_shape)
			_selectedGroup.visible = _selected

		}

		/**重新計算**/
		protected function render(scale=1):void
		{
			updateLine(scale)
		}

		/**更新框線**/
		protected function updateLine(scale=1):void
		{
			if(_contents && _shape){
				_shape.graphics.lineStyle(10,0xFFffFF);
					_shape.graphics.clear();

					System.gc();

					var _w:Number = _contents.width
					var _h:Number = _contents.height
					var _halfW:Number = _w/2
					var _halfH:Number= _h/2

					_shape.graphics.lineStyle(2 * scale,0xFFFFFF);
					//_shape.graphics.drawRoundRect( -_halfW, -_halfH, _w, _h, 10 );
					_shape.graphics.drawRect(-_halfW, -_halfH, _w, _h);

					//trace("scale",scale)

			}
		}

		public function setCtrlButtonInitByTexture(upTexture:Texture , downTexture:Texture = null ):void
		{
			_useCtrlButton = true

			if ( upTexture !=null)
			{
				_ctrlButton = new Button(upTexture,"",downTexture);

				setCtrlButtonInitByObject(_ctrlButton )
			}else{
				throw new ArgumentError("Texture is Null!")
			}
		}

		/** 設定控制按鈕 by 物件 可以是 image sprite button **/
		public function setCtrlButtonInitByObject( obj:DisplayObject ):void
		{
			_useCtrlButton = true

			if (_contents)
			{
				_ctrlButton = obj

				var _w:Number = _contents.width
				var _h:Number = _contents.height
				var _halfW:Number = _w/2
				var _halfH:Number= _h/2
				//放置右上角
				_ctrlButton.x = int(_halfW)
				_ctrlButton.y = - int(_halfH)

				//置中對齊
				_ctrlButton.x -= int(_ctrlButton.width/2)
				_ctrlButton.y -= int(_ctrlButton.height/2)

				_selectedGroup.addChild(_ctrlButton)
			}
		}

		protected function onTouch(event:TouchEvent):void
		{
			var touches:Vector.<Touch> = event.getTouches(this, TouchPhase.MOVED);
			var touch:Touch

			touch = event.getTouch(this, TouchPhase.BEGAN);
			onTouchBegan(touch)

			//一隻手指
			if (touches.length == 1)
			{
				//檢查是否是Ctrl
				if(isTouchCtrlButton(event.target) )
				{
					resizeAndRotationByCtrlButton(touches)
				}
				else
				{
					//一隻手指平移
					var delta:Point = touches[0].getMovement(parent);

					x += delta.x;
					y += delta.y;
				}
			}
			else if (touches.length == 2)
			{
				//兩隻手指
				resizeAndRotationByTwoFingers(touches)
			}

			touch  = event.getTouch(this, TouchPhase.ENDED);
			onTouchEnd(touch)

		}

		/**當touch開始**/
		protected function onTouchBegan(touch:Touch):void
		{
			//touch 開始要做的事情
			if(touch){
				if(_touchBringToFront){
					parent.addChild(this);
				}
				selected = true
			}

		}

		/**當touch結束**/
		protected function onTouchEnd(touch:Touch):void
		{

		}

		/**檢查是否touch 控制按鈕**/
		protected function isTouchCtrlButton(target:*):Boolean
		{

			var _do		:DisplayObject
			var _doc	:DisplayObjectContainer

			if(_ctrlButton is  DisplayObjectContainer){
				_doc = _ctrlButton as DisplayObjectContainer
				return _doc.contains(target)
			}
			else if(_ctrlButton is DisplayObject)
			{
				_do = _ctrlButton as DisplayObject
				return _do == target
			}
			return false
		}

		/** 單隻手指使用控制按鈕 放大縮小旋轉 **/
		protected function resizeAndRotationByCtrlButton(touches:Vector.<Touch>):void
		{

			var touchA:Touch = touches[0];
			var touchB:Touch = touchA.clone();		//模擬B點
			var n:Point = touchA.getLocation(this);
			//鏡射A點坐標
			touchB.globalX = n.x * 1
			touchB.globalY = n.y * -1

			var currentPosA:Point  = touchA.getLocation(this);
			var previousPosA:Point = touchA.getPreviousLocation(this);

			//鏡射A點
			var currentPosB:Point  = currentPosA.clone();
			currentPosB.x *=-1;
			currentPosB.y *=-1;
			//鏡射A點
			var previousPosB:Point  = previousPosA.clone();
			previousPosB.x *=-1;
			previousPosB.y *=-1;

			var currentVector:Point  = currentPosA.subtract(currentPosB);
			var previousVector:Point = previousPosA.subtract(previousPosB);

			var currentAngle:Number  = Math.atan2(currentVector.y, currentVector.x);
			var previousAngle:Number = Math.atan2(previousVector.y, previousVector.x);

			var deltaAngle:Number = currentAngle - previousAngle;

			// rotate
			rotation += deltaAngle;

			// scale
			var sizeDiff:Number = currentVector.length / previousVector.length;
			scaleX *= sizeDiff;
			scaleY *= sizeDiff;

			//_ctrlButton 保持原比例
			if(_ctrlButton){
				_ctrlButton.scaleX /= sizeDiff
				_ctrlButton.scaleY /= sizeDiff
			}

			render(_ctrlButton.scaleX)

		}

		/** 使用兩隻手指 使用放大縮小旋轉 保留官方功能 **/
		protected function resizeAndRotationByTwoFingers(touches:Vector.<Touch>):void
		{
			//保留官方原本兩隻手指操作的功能
			// two fingers touching -> rotate and scale
			var touchA:Touch = touches[0];
			var touchB:Touch = touches[1];

			var currentPosA:Point  = touchA.getLocation(parent);
			var previousPosA:Point = touchA.getPreviousLocation(parent);
			var currentPosB:Point  = touchB.getLocation(parent);
			var previousPosB:Point = touchB.getPreviousLocation(parent);

			var currentVector:Point  = currentPosA.subtract(currentPosB);
			var previousVector:Point = previousPosA.subtract(previousPosB);

			var currentAngle:Number  = Math.atan2(currentVector.y, currentVector.x);
			var previousAngle:Number = Math.atan2(previousVector.y, previousVector.x);
			var deltaAngle:Number = currentAngle - previousAngle;

			// update pivot point based on previous center
			var previousLocalA:Point  = touchA.getPreviousLocation(this);
			var previousLocalB:Point  = touchB.getPreviousLocation(this);
			trace(previousLocalA , previousLocalB)

			pivotX = (previousLocalA.x + previousLocalB.x) * 0.5;
			pivotY = (previousLocalA.y + previousLocalB.y) * 0.5;

			// update location based on the current center
			x = (currentPosA.x + currentPosB.x) * 0.5;
			y = (currentPosA.y + currentPosB.y) * 0.5;

			// rotate
			rotation += deltaAngle;

			// scale
			var sizeDiff:Number = currentVector.length / previousVector.length;
			scaleX *= sizeDiff;
			scaleY *= sizeDiff;

			render(_ctrlButton.scaleX)
		}

		public override function dispose():void
		{
			removeEventListener(TouchEvent.TOUCH, onTouch);
			if(_contents){
				_contents.dispose()
			}
			super.dispose();
		}
	}
}

package lambmei.starling.display
{
	import flash.display.MovieClip;
	import flash.geom.Point;
	import flash.system.System;

	import starling.display.Button;
	import starling.display.DisplayObject;
	import starling.display.DisplayObjectContainer;
	import starling.display.Shape;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.textures.Texture;
	import starling.utils.Line;

	[Event(name="handleSheetSelected"		,type="starling.events.Event")]

	public class HandleSheet extends Sprite
	{

		/** 點擊 將物件移動到前方 **/
		protected var _touchBringToFront:Boolean
		public function get touchBringToFront():Boolean { return _touchBringToFront;}
		public function set touchBringToFront(value:Boolean):void{ _touchBringToFront = value; }

		/** 是否選擇 **/
		protected var _selected:Boolean
		public function get selected():Boolean { return _selected;}
		public function set selected(value:Boolean):void
		{
			if(value!=_selected){
				_selected = value;
				_selectedGroup.visible = _selected
			}
		}

		/** 是否有使用 控制按鈕 Read Only **/
		protected var _useCtrlButton:Boolean
		public function get useCtrlButton():Boolean
		{
			return _useCtrlButton;
		}

		//Const Vars
		protected static const CTRL_BUTTON_NAME:String = "HandlectrlBtn"
		public static const ALIGN_CENTER:String = "center"
		public static const ALIGN_LT:String = "LT"

		public static const EVENT_SELECTED:String = "handleSheetSelected"

		//UI
		protected var _contents:DisplayObject
		protected var _ctrlButton:DisplayObject
		protected var _selectedGroup:Sprite
		protected var _shape:Shape

		public function HandleSheet(contents:DisplayObject=null)
		{
			addEventListener(TouchEvent.TOUCH, onTouch);
			useHandCursor = true;

			_touchBringToFront = true

			if (contents)
			{
				var _w:Number = contents.width
				var _h:Number = contents.height
				var _halfW:Number = _w/2
				var _halfH:Number= _h/2
				//將物件置中
				contents.x = int(_halfW * -1);
				contents.y = int(_halfH * -1);
				addChild(contents);
				_contents = contents

			}
			//init SelectGroup
			initSelectedGroup()
		}

		/** 初始化選擇的群組**/
		protected function initSelectedGroup():void
		{

			_selectedGroup = new Sprite()
			this.addChild(this._selectedGroup);

			_shape = new Shape()

			_selectedGroup.addChild(_shape)
			_selectedGroup.visible = _selected

		}

		/**重新計算**/
		protected function render(sizeDiff:Number , deltaAngle:Number):void
		{
			rotation += deltaAngle;

			scaleX *= sizeDiff;
			scaleY *= sizeDiff;

			//_ctrlButton 保持原比例
			if(_ctrlButton){
				_ctrlButton.scaleX /= sizeDiff
				_ctrlButton.scaleY /= sizeDiff

				//size 變更後需要重新定位
				updateCtrlButtonPosition()
			}

			updateLine(_ctrlButton.scaleX)
		}

		/**計算CtrlButton 放置位置**/
		protected function updateCtrlButtonPosition():void
		{
			if(_contents && _ctrlButton){

				var _w:Number = _contents.width
				var _h:Number = _contents.height
				var _halfW:Number = _w/2
				var _halfH:Number= _h/2

				//放置右上角
				_ctrlButton.x = int(_halfW)
				_ctrlButton.y = - int(_halfH)
				//置中對齊
				_ctrlButton.x -= int(_ctrlButton.width/2)
				_ctrlButton.y -= int(_ctrlButton.height/2)

			}
		}

		/**更新框線**/
		protected function updateLine(sizeRate:Number):void
		{
			if(_contents && _shape){
				_shape.graphics.lineStyle(10,0xFFffFF);
					_shape.graphics.clear();

					System.gc();

					var _w:Number = _contents.width
					var _h:Number = _contents.height
					var _halfW:Number = _w/2
					var _halfH:Number= _h/2

					_shape.graphics.lineStyle(2 * sizeRate ,0xFFFFFF);
					//_shape.graphics.drawRoundRect( -_halfW, -_halfH, _w, _h, 10 );
					_shape.graphics.drawRect(-_halfW, -_halfH, _w, _h);

					//trace("scale",scale)

			}
		}

		public function setCtrlButtonInitByTexture(upTexture:Texture , downTexture:Texture = null ):void
		{
			_useCtrlButton = true

			if ( upTexture !=null)
			{
				_ctrlButton = new Button(upTexture,"",downTexture);

				setCtrlButtonInitByObject(_ctrlButton )
			}else{
				throw new ArgumentError("Texture is Null!")
			}
		}

		/** 設定控制按鈕 by 物件 可以是 image sprite button **/
		public function setCtrlButtonInitByObject( obj:DisplayObject ):void
		{
			_useCtrlButton = true

			if (_contents)
			{
				//避免讓外部物件Size != 1 影響計算
				var _container:Sprite = new Sprite()
				_container.addChild(obj)
				_ctrlButton = _container
				//定位
				updateCtrlButtonPosition()

				_selectedGroup.addChild(_ctrlButton)
			}
		}

		protected function onTouch(event:TouchEvent):void
		{
			var touches:Vector.<Touch> = event.getTouches(this, TouchPhase.MOVED);
			var touch:Touch

			touch = event.getTouch(this, TouchPhase.BEGAN);
			onTouchBegan(touch)

			//一隻手指
			if (touches.length == 1)
			{
				//檢查是否是Ctrl
				if(isTouchCtrlButton(event.target) )
				{
					resizeAndRotationByCtrlButton(touches)
				}
				else
				{
					//一隻手指平移
					var delta:Point = touches[0].getMovement(parent);

					x += delta.x;
					y += delta.y;
				}
			}
			else if (touches.length == 2)
			{
				//兩隻手指
				resizeAndRotationByTwoFingers(touches)
			}

			touch  = event.getTouch(this, TouchPhase.ENDED);
			onTouchEnd(touch)

		}

		/**當touch開始**/
		protected function onTouchBegan(touch:Touch):void
		{
			//touch 開始要做的事情
			if(touch){
				if(_touchBringToFront){
					parent.addChild(this);
				}
				selected = true

				render(1, 0)
				dispatchEventWith(EVENT_SELECTED,false)
			}

		}

		/**當touch結束**/
		protected function onTouchEnd(touch:Touch):void
		{

		}

		/**檢查是否touch 控制按鈕**/
		protected function isTouchCtrlButton(target:*):Boolean
		{

			var _do		:DisplayObject
			var _doc	:DisplayObjectContainer

			if(_ctrlButton is  DisplayObjectContainer){
				_doc = _ctrlButton as DisplayObjectContainer
				return _doc.contains(target)
			}
			else if(_ctrlButton is DisplayObject)
			{
				_do = _ctrlButton as DisplayObject
				return _do == target
			}
			return false
		}

		/** 單隻手指使用控制按鈕 放大縮小旋轉 **/
		protected function resizeAndRotationByCtrlButton(touches:Vector.<Touch>):void
		{

			var touchA:Touch = touches[0];
			var touchB:Touch = touchA.clone();		//模擬B點
			var n:Point = touchA.getLocation(this);
			//鏡射A點坐標
			touchB.globalX = n.x * 1
			touchB.globalY = n.y * -1

			var currentPosA:Point  = touchA.getLocation(this);
			var previousPosA:Point = touchA.getPreviousLocation(this);

			//鏡射A點
			var currentPosB:Point  = currentPosA.clone();
			currentPosB.x *=-1;
			currentPosB.y *=-1;
			//鏡射A點
			var previousPosB:Point  = previousPosA.clone();
			previousPosB.x *=-1;
			previousPosB.y *=-1;

			var currentVector:Point  = currentPosA.subtract(currentPosB);
			var previousVector:Point = previousPosA.subtract(previousPosB);

			var currentAngle:Number  = Math.atan2(currentVector.y, currentVector.x);
			var previousAngle:Number = Math.atan2(previousVector.y, previousVector.x);

			var deltaAngle:Number = currentAngle - previousAngle;

			// rotate
			//rotation += deltaAngle;

			// scale
			var sizeDiff:Number = currentVector.length / previousVector.length;
//			scaleX *= sizeDiff;
//			scaleY *= sizeDiff;

			render(sizeDiff , deltaAngle)

		}

		/** 使用兩隻手指 使用放大縮小旋轉 保留官方功能 **/
		protected function resizeAndRotationByTwoFingers(touches:Vector.<Touch>):void
		{
			//保留官方原本兩隻手指操作的功能
			// two fingers touching -> rotate and scale
			var touchA:Touch = touches[0];
			var touchB:Touch = touches[1];

			var currentPosA:Point  = touchA.getLocation(parent);
			var previousPosA:Point = touchA.getPreviousLocation(parent);
			var currentPosB:Point  = touchB.getLocation(parent);
			var previousPosB:Point = touchB.getPreviousLocation(parent);

			var currentVector:Point  = currentPosA.subtract(currentPosB);
			var previousVector:Point = previousPosA.subtract(previousPosB);

			var currentAngle:Number  = Math.atan2(currentVector.y, currentVector.x);
			var previousAngle:Number = Math.atan2(previousVector.y, previousVector.x);
			var deltaAngle:Number = currentAngle - previousAngle;

			// update pivot point based on previous center
			var previousLocalA:Point  = touchA.getPreviousLocation(this);
			var previousLocalB:Point  = touchB.getPreviousLocation(this);
			trace(previousLocalA , previousLocalB)

			pivotX = (previousLocalA.x + previousLocalB.x) * 0.5;
			pivotY = (previousLocalA.y + previousLocalB.y) * 0.5;

			// update location based on the current center
			x = (currentPosA.x + currentPosB.x) * 0.5;
			y = (currentPosA.y + currentPosB.y) * 0.5;

			// rotate
//			rotation += deltaAngle;

			// scale
			var sizeDiff:Number = currentVector.length / previousVector.length;
//			scaleX *= sizeDiff;
//			scaleY *= sizeDiff;

			render(sizeDiff , deltaAngle)
		}

		public override function dispose():void
		{
			removeEventListener(TouchEvent.TOUCH, onTouch);
			if(_contents){
				_contents.dispose()
			}
			super.dispose();
		}
	}
}

fix CtrlButton 位置 SELECT 觸發事件

fix CtrlButton 位置 SELECT 觸發事件


package com.mangui.HLS.muxing {

    import com.mangui.HLS.muxing.*;
    import com.mangui.HLS.utils.*;
    import flash.utils.ByteArray;

    /** Representation of a Packetized Elementary Stream. **/
    public class PES {

        /** Timescale of pts/dts is 90khz. **/
        public static var TIMESCALE:Number = 90;

        /** Is it AAC audio or AVC video. **/
        public var audio:Boolean;
        /** The PES data (including headers). **/
        public var data:ByteArray;
        /** Start of the payload. **/
        public var payload:Number;
        /** Timestamp from the PTS header. **/
        public var pts:Number;
        /** Timestamp from the DTS header. **/
        public var dts:Number;

        /** Save the first chunk of PES data. **/
        public function PES(dat:ByteArray,aud:Boolean) {
            data = dat;
            audio = aud;
        };

        /** Append PES data from additional TS packets. **/
        public function append(dat:ByteArray):void {
            data.writeBytes(dat,0,0);
        };

        /** When all data is appended, parse the PES headers. **/
        public function parse():void {
            data.position = 0;
            // Start code prefix and packet ID.
            var prefix:Number = data.readUnsignedInt();
            if((audio && (prefix > 448 || prefix < 445)) ||
                (!audio && prefix != 480 && prefix != 490)) {
                throw new Error("PES start code not found or not AAC/AVC: " + prefix);
            }
            // Ignore packet length and marker bits.
            data.position += 3;
            // Check for PTS
            var flags:uint = (data.readUnsignedByte() & 192) >> 6;
            if(flags != 2 && flags != 3) {
                throw new Error("No PTS/DTS in this PES packet");
            }
            // Check PES header length
            var length:uint = data.readUnsignedByte();
            // Grab the timestamp from PTS data (spread out over 5 bytes):
            // XXXX---X -------- -------X -------- -------X
            var _pts:uint = ((data.readUnsignedByte() & 14) << 29) +
                ((data.readUnsignedShort() & 65535) << 14) +
                ((data.readUnsignedShort() & 65535) >> 1);
            length -= 5;
            var _dts:uint = _pts;
            if(flags == 3) {
                // Grab the DTS (like PTS)
                _dts = ((data.readUnsignedByte() & 14) << 29) +
                    ((data.readUnsignedShort() & 65535) << 14) +
                    ((data.readUnsignedShort() & 65535) >> 1);
                length -= 5;
            }
            pts = Math.round(_pts / PES.TIMESCALE);
            dts = Math.round(_dts / PES.TIMESCALE);
            // Skip other header data and parse payload.
            data.position += length;
            payload = data.position;
        };

    }

}

package com.mangui.HLS.muxing {

    import com.mangui.HLS.muxing.*;
    import com.mangui.HLS.utils.*;
    import flash.utils.ByteArray;

    /** Representation of a Packetized Elementary Stream. **/
    public class PES {

        /** Timescale of pts/dts is 90khz. **/
        public static var TIMESCALE:Number = 90;

        /** Is it AAC audio or AVC video. **/
        public var audio:Boolean;
        /** The PES data (including headers). **/
        public var data:ByteArray;
        /** Start of the payload. **/
        public var payload:Number;
        /** Timestamp from the PTS header. **/
        public var pts:Number;
        /** Timestamp from the DTS header. **/
        public var dts:Number;

        /** Save the first chunk of PES data. **/
        public function PES(dat:ByteArray,aud:Boolean) {
            data = dat;
            audio = aud;
        };

        /** Append PES data from additional TS packets. **/
        public function append(dat:ByteArray):void {
            data.writeBytes(dat,0,0);
        };

        /** When all data is appended, parse the PES headers. **/
        public function parse():void {
            data.position = 0;
            // Start code prefix and packet ID.
            var prefix:Number = data.readUnsignedInt();
            if((audio && (prefix > 448 || prefix < 445)) ||
                (!audio && prefix != 480 && prefix != 490)) {
                throw new Error("PES start code not found or not AAC/AVC: " + prefix);
            }
            // Ignore packet length and marker bits.
            data.position += 3;
            // Check for PTS
            var flags:uint = (data.readUnsignedByte() & 192) >> 6;
            if(flags != 2 && flags != 3) {
                throw new Error("No PTS/DTS in this PES packet");
            }
            // Check PES header length
            var length:uint = data.readUnsignedByte();
            // Grab the timestamp from PTS data (spread out over 5 bytes):
            // XXXX---X -------- -------X -------- -------X
            var _pts:uint = ((data.readUnsignedByte() & 14) << 29) +
                ((data.readUnsignedShort() & 65534) << 14) +
                ((data.readUnsignedShort() & 65534) >> 1);
            length -= 5;
            var _dts:uint = _pts;
            if(flags == 3) {
                // Grab the DTS (like PTS)
                _dts = ((data.readUnsignedByte() & 14) << 29) +
                    ((data.readUnsignedShort() & 65534) << 14) +
                    ((data.readUnsignedShort() & 65534) >> 1);
                length -= 5;
            }
            pts = Math.round(_pts / PES.TIMESCALE);
            dts = Math.round(_dts / PES.TIMESCALE);
            // Skip other header data and parse payload.
            data.position += length;
            payload = data.position;
        };

    }

}

correct PTS/DTS parsing according to http://dvd.sourceforge.net/dvdinfo/pes-hdr.html, bit 0 is not used. change mask accordingly.