/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.controller {
    import org.mangui.hls.constant.HLSLoaderTypes;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSLoadMetrics;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that manages buffer threshold values (minBufferLength/lowBufferLength)
     */
    public class BufferThresholdController {
        /** Reference to the HLS controller. **/
        private var _hls : HLS;
        // max nb of samples used for bw checking. the bigger it is, the more conservative it is.
        private static const MAX_SAMPLES : int = 30;
        private var _bw : Vector.<Number>;
        private var _nbSamples : uint;
        private var _targetduration : Number;
        private var _minBufferLength : Number;

        /** Create the loader. **/
        public function BufferThresholdController(hls : HLS) : void {
            _hls = hls;
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
            _hls.addEventListener(HLSEvent.TAGS_LOADED, _fragmentLoadedHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler);
        };

        public function dispose() : void {
            _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
            _hls.removeEventListener(HLSEvent.TAGS_LOADED, _fragmentLoadedHandler);
            _hls.removeEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler);
        }

        public function get minBufferLength() : Number {
            if (HLSSettings.minBufferLength == -1) {
                return _minBufferLength;
            } else {
                return HLSSettings.minBufferLength;
            }
        }

        public function get lowBufferLength() : Number {
            if (HLSSettings.minBufferLength == -1) {
                // in automode, low buffer threshold should be less than min auto buffer
                return Math.min(minBufferLength / 2, HLSSettings.lowBufferLength);
            } else {
                return HLSSettings.lowBufferLength;
            }
        }

        private function _manifestLoadedHandler(event : HLSEvent) : void {
            _nbSamples = 0;
            _targetduration = event.levels[_hls.startLevel].targetduration;
            _bw = new Vector.<Number>(MAX_SAMPLES,true);
            _minBufferLength = _targetduration;
        };

        private function _fragmentLoadedHandler(event : HLSEvent) : void {
            var metrics : HLSLoadMetrics = event.loadMetrics;
            // only monitor main fragment metrics for buffer threshold computing
            if(metrics.type == HLSLoaderTypes.FRAGMENT_MAIN) {
                var cur_bw : int = metrics.bandwidth;
                _bw[_nbSamples % MAX_SAMPLES] = cur_bw;
                _nbSamples++;

                // compute min bw on MAX_SAMPLES
                var minBw : Number = Number.POSITIVE_INFINITY;
                var samples_max : int = Math.min(_nbSamples, MAX_SAMPLES);
                for (var i : int = 0; i < samples_max; i++) {
                    minBw = Math.min(minBw, _bw[i]);
                }

                // give more weight to current bandwidth
                var bw_ratio : Number = 2 * cur_bw / (minBw + cur_bw);

                /* predict time to dl next segment using a conservative approach.
                 *
                 * heuristic is as follow :
                 *
                 * time to dl next segment = time to dl current segment *  (playlist target duration / current segment duration) * bw_ratio
                 *                           \---------------------------------------------------------------------------------/
                 *                                  this part is a simple rule by 3, assuming we keep same dl bandwidth
                 *  bw ratio is the conservative factor, assuming that next segment will be downloaded with min bandwidth
                 */
                _minBufferLength = metrics.processing_duration * (_targetduration / metrics.duration) * bw_ratio;
                // avoid min > max
                if (HLSSettings.maxBufferLength) {
                    _minBufferLength = Math.min(HLSSettings.maxBufferLength, _minBufferLength);
                }

                // avoid _minBufferLength > minBufferLengthCapping
                if (HLSSettings.minBufferLengthCapping > 0) {
                    _minBufferLength = Math.min(HLSSettings.minBufferLengthCapping, _minBufferLength);
                }

                CONFIG::LOGGING {
                    Log.debug2("AutoBufferController:minBufferLength:" + _minBufferLength);
                }
            };
        }
    }
}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.controller {
    import org.mangui.hls.constant.HLSLoaderTypes;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSLoadMetrics;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Class that manages buffer threshold values (minBufferLength/lowBufferLength)
     */
    public class BufferThresholdController {
        /** Reference to the HLS controller. **/
        private var _hls : HLS;
        private var _targetduration : Number;
        private var _minBufferLength : Number;

        /** Create the loader. **/
        public function BufferThresholdController(hls : HLS) : void {
            _hls = hls;
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
            _hls.addEventListener(HLSEvent.TAGS_LOADED, _fragmentLoadedHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler);
        };

        public function dispose() : void {
            _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
            _hls.removeEventListener(HLSEvent.TAGS_LOADED, _fragmentLoadedHandler);
            _hls.removeEventListener(HLSEvent.FRAGMENT_LOADED, _fragmentLoadedHandler);
        }

        public function get minBufferLength() : Number {
            if (HLSSettings.minBufferLength == -1) {
                return _minBufferLength;
            } else {
                return HLSSettings.minBufferLength;
            }
        }

        public function get lowBufferLength() : Number {
            if (HLSSettings.minBufferLength == -1) {
                // in automode, low buffer threshold should be less than min auto buffer
                return Math.min(minBufferLength / 2, HLSSettings.lowBufferLength);
            } else {
                return HLSSettings.lowBufferLength;
            }
        }

        private function _manifestLoadedHandler(event : HLSEvent) : void {
            _targetduration = event.levels[_hls.startLevel].targetduration;
            _minBufferLength = _targetduration;
        };

        private function _fragmentLoadedHandler(event : HLSEvent) : void {
            var metrics : HLSLoadMetrics = event.loadMetrics;
            // only monitor main fragment metrics for buffer threshold computing
            if(metrics.type == HLSLoaderTypes.FRAGMENT_MAIN) {
                /* set min buf len to be the time to process a complete segment, using current processing rate */
                _minBufferLength = metrics.processing_duration * (_targetduration / metrics.duration);
                // avoid min > max
                if (HLSSettings.maxBufferLength) {
                    _minBufferLength = Math.min(HLSSettings.maxBufferLength, _minBufferLength);
                }

                // avoid _minBufferLength > minBufferLengthCapping
                if (HLSSettings.minBufferLengthCapping > 0) {
                    _minBufferLength = Math.min(HLSSettings.minBufferLengthCapping, _minBufferLength);
                }

                CONFIG::LOGGING {
                    Log.debug2("AutoBufferController:minBufferLength:" + _minBufferLength);
                }
            };
        }
    }
}

BufferThresholdController: remove conservative factor as the algorithm is already really conservative...
minBufferLength is the time to process a complete segment (of duration EXT-X-TARGETDURATION), using current processing rate


package aerys.minko.type
{
	public final class Signal
	{
		private var _name					: String;
        private var _enabled        		: Boolean;
		private var _disableWhenNoCallbacks	: Boolean;

		private var _callbacks				: Vector.<Function>;
		private var _numCallbacks			: uint;

		private var _executed				: Boolean;
		private var _numAdded				: uint;
		private var _toAdd					: Vector.<Function>;
		private var _numRemoved				: uint;
		private var _toRemove				: Vector.<Function>;

        public function get enabled() : Boolean
        {
            return _enabled;
        }
        public function set enabled(value : Boolean) : void
        {
            _enabled = value;
        }

		public function get numCallbacks() : uint
		{
			return _numCallbacks;
		}

		public function Signal(name 					: String,
							   enabled 					: Boolean 	= true,
							   disableWhenNoCallbacks	: Boolean	= false)
		{
			_name = name;
            _enabled = enabled;
			_disableWhenNoCallbacks = disableWhenNoCallbacks;

            _callbacks = new <Function>[];
		}

		public function add(callback : Function) : void
		{
            if (_callbacks.indexOf(callback) >= 0)
            {
                var removeIndex : int = _toRemove ? _toRemove.indexOf(callback) : -1;

                // if that callback is in the temp. remove list, we simply remove it from this list
                // instead of removing/adding it all over again
                if (removeIndex >= 0)
                {
                    --_numRemoved;
                    _toRemove[removeIndex] = _toRemove[_numRemoved];
                    _toRemove.length = _numRemoved;

                    return;
                }
                else
                    throw new Error('The same callback cannot be added twice.');
            }

			if (_executed)
			{
				if (_toAdd)
					_toAdd.push(callback);
				else
					_toAdd = new <Function>[callback];
				++_numAdded;

				return ;
			}

			_callbacks[_numCallbacks] = callback;
			++_numCallbacks;

			if (_numCallbacks == 1 && _disableWhenNoCallbacks)
				_enabled = true;
		}

		public function remove(callback : Function) : void
		{
			var index : int = _callbacks.indexOf(callback);

			if (index < 0)
            {
                var addIndex : int = _toAdd ? _toAdd.indexOf(callback) : -1;

                // if that callback is in the temp. add list, we simply remove it from this list
                // instead of adding/removing it all over again
                if (addIndex >= 0)
                {
                    --_numAdded;
                    _toAdd[addIndex] = _toAdd[_numAdded];
                    _toAdd.length = _numAdded;
                }
                else
    				throw new Error('This callback does not exist.');
            }

			if (_executed)
			{
				if (_toRemove)
					_toRemove.push(callback);
				else
					_toRemove = new <Function>[callback];
				++_numRemoved;

				return ;
			}

			--_numCallbacks;
			_callbacks[index] = _callbacks[_numCallbacks];
			_callbacks.length = _numCallbacks;

			if (!_numCallbacks && _disableWhenNoCallbacks)
				_enabled = false;
		}

		public function hasCallback(callback : Function) : Boolean
		{
			return _callbacks.indexOf(callback) >= 0;
		}

		public function execute(...params) : void
		{
			if (_numCallbacks && _enabled)
			{
				_executed = true;
				for (var i : uint = 0; i < _numCallbacks; ++i)
					_callbacks[i].apply(null, params);
				_executed = false;

				for (i = 0; i < _numAdded; ++i)
					add(_toAdd[i]);
				_numAdded = 0;
				_toAdd = null;

				for (i = 0; i < _numRemoved; ++i)
					remove(_toRemove[i]);
				_numRemoved = 0;
				_toRemove = null;
			}
		}
	}
}

package aerys.minko.type
{
	public final class Signal
	{
		private var _name					: String;
        private var _enabled        		: Boolean;
		private var _disableWhenNoCallbacks	: Boolean;

		private var _callbacks				: Vector.<Function>;
		private var _numCallbacks			: uint;

		private var _executed				: Boolean;
		private var _numAdded				: uint;
		private var _toAdd					: Vector.<Function>;
		private var _numRemoved				: uint;
		private var _toRemove				: Vector.<Function>;

        public function get enabled() : Boolean
        {
            return _enabled;
        }
        public function set enabled(value : Boolean) : void
        {
            _enabled = value;
        }

		public function get numCallbacks() : uint
		{
			return _numCallbacks;
		}

		public function Signal(name 					: String,
							   enabled 					: Boolean 	= true,
							   disableWhenNoCallbacks	: Boolean	= false)
		{
			_name = name;
            _enabled = enabled;
			_disableWhenNoCallbacks = disableWhenNoCallbacks;

            _callbacks = new <Function>[];
		}

		public function add(callback : Function) : void
		{
            if (_callbacks.indexOf(callback) >= 0)
            {
                var removeIndex : int = _toRemove ? _toRemove.indexOf(callback) : -1;

                // if that callback is in the temp. remove list, we simply remove it from this list
                // instead of removing/adding it all over again
                if (removeIndex >= 0)
                {
                    --_numRemoved;
                    _toRemove[removeIndex] = _toRemove[_numRemoved];
                    _toRemove.length = _numRemoved;

                    return;
                }
                else
                    throw new Error('The same callback cannot be added twice.');
            }

			if (_executed)
			{
				if (_toAdd)
					_toAdd.push(callback);
				else
					_toAdd = new <Function>[callback];
				++_numAdded;

				return ;
			}

			_callbacks[_numCallbacks] = callback;
			++_numCallbacks;

			if (_numCallbacks == 1 && _disableWhenNoCallbacks)
				_enabled = true;
		}

		public function remove(callback : Function) : void
		{
			var index : int = _callbacks.indexOf(callback);

			if (index < 0)
            {
                var addIndex : int = _toAdd ? _toAdd.indexOf(callback) : -1;

                // if that callback is in the temp. add list, we simply remove it from this list
                // instead of adding/removing it all over again
                if (addIndex >= 0)
                {
                    --_numAdded;
                    _toAdd[addIndex] = _toAdd[_numAdded];
                    _toAdd.length = _numAdded;
                }
                else
    				throw new Error('This callback does not exist.');
            }

			if (_executed)
			{
				if (_toRemove)
					_toRemove.push(callback);
				else
					_toRemove = new <Function>[callback];
				++_numRemoved;

				return ;
			}

			--_numCallbacks;
			_callbacks[index] = _callbacks[_numCallbacks];
			_callbacks.length = _numCallbacks;

			if (!_numCallbacks && _disableWhenNoCallbacks)
				_enabled = false;
		}

		public function hasCallback(callback : Function) : Boolean
		{
			return _callbacks.indexOf(callback) >= 0;
		}

		public function execute(...p) : void
		{
			if (_numCallbacks && _enabled)
			{
				_executed = true;
				for (var i : uint = 0; i < _numCallbacks; ++i)
                {
                    switch (p.length)
                    {
    					case 0:
                            _callbacks[i]();
                            break ;
                        case 1:
                            _callbacks[i](p[0]);
                            break ;
                        case 2:
                            _callbacks[i](p[0], p[1]);
                            break ;
                        case 3:
                            _callbacks[i](p[0], p[1], p[2]);
                            break ;
                        case 4:
                            _callbacks[i](p[0], p[1], p[2], p[3]);
                            break ;
                        case 5:
                            _callbacks[i](p[0], p[1], p[2], p[3], p[4]);
                            break ;
                        case 6:
                            _callbacks[i](p[0], p[1], p[2], p[3], p[4], p[5]);
                            break ;
                        case 7:
                            _callbacks[i](p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
                            break ;
                        case 8:
                            _callbacks[i](p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
                            break ;
                        case 9:
                            _callbacks[i](p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]);
                            break ;
                        case 10:
                            _callbacks[i](p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
                            break ;
                    }
//                   _callbacks[i].apply(null, params);
                }
				_executed = false;

				for (i = 0; i < _numAdded; ++i)
					add(_toAdd[i]);
				_numAdded = 0;
				_toAdd = null;

				for (i = 0; i < _numRemoved; ++i)
					remove(_toRemove[i]);
				_numRemoved = 0;
				_toRemove = null;
			}
		}
	}
}

change Signal.execute to avoid calling Function.apply but drop support for callbacks with more than 10 arguments


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mangui.hls.loader {
    import org.mangui.hls.playlist.DataUri;
    import org.mangui.hls.playlist.AltAudioTrack;
    import org.mangui.hls.playlist.Manifest;
    import org.mangui.hls.constant.HLSPlayStates;
    import org.mangui.hls.constant.HLSTypes;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.hls.event.HLSError;
    import org.mangui.hls.model.Level;
    import org.mangui.hls.model.Fragment;
    import org.mangui.hls.HLS;
    import org.mangui.hls.HLSSettings;

    import flash.events.*;
    import flash.net.*;
    import flash.utils.*;

    CONFIG::LOGGING {
        import org.mangui.hls.utils.Log;
    }
    /** Loader for hls manifests. **/
    public class LevelLoader {
        /** Reference to the hls framework controller. **/
        private var _hls : HLS;
        /** levels vector. **/
        private var _levels : Vector.<Level>;
        /** Object that fetches the manifest. **/
        private var _urlloader : URLLoader;
        /** Link to the M3U8 file. **/
        private var _url : String;
        /** are all playlists filled ? **/
        private var _canStart : Boolean;
        /** Timeout ID for reloading live playlists. **/
        private var _timeoutID : uint;
        /** Streaming type (live, ondemand). **/
        private var _type : String;
        /** last reload manifest time **/
        private var _reload_playlists_timer : uint;
        /** current level **/
        private var _current_level : int;
        /** reference to manifest being loaded **/
        private var _manifest_loading : Manifest;
        /** is this loader closed **/
        private var _closed : Boolean = false;
        /* playlist retry timeout */
        private var _retry_timeout : Number;
        private var _retry_count : int;
        /* alt audio tracks */
        private var _alt_audio_tracks : Vector.<AltAudioTrack>;

        /** Setup the loader. **/
        public function LevelLoader(hls : HLS) {
            _hls = hls;
            _hls.addEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.addEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
            _levels = new Vector.<Level>();
            _urlloader = new URLLoader();
            _urlloader.addEventListener(Event.COMPLETE, _loaderHandler);
            _urlloader.addEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
            _urlloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
        };

        public function dispose() : void {
            _close();
            _urlloader.removeEventListener(Event.COMPLETE, _loaderHandler);
            _urlloader.removeEventListener(IOErrorEvent.IO_ERROR, _errorHandler);
            _urlloader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _errorHandler);
            _hls.removeEventListener(HLSEvent.PLAYBACK_STATE, _stateHandler);
            _hls.removeEventListener(HLSEvent.LEVEL_SWITCH, _levelSwitchHandler);
        }

        /** Loading failed; return errors. **/
        private function _errorHandler(event : ErrorEvent) : void {
            var txt : String;
            var code : int;
            if (event is SecurityErrorEvent) {
                code = HLSError.MANIFEST_LOADING_CROSSDOMAIN_ERROR;
                txt = "Cannot load M3U8: crossdomain access denied:" + event.text;
            } else if (event is IOErrorEvent && _levels.length && (HLSSettings.manifestLoadMaxRetry == -1 || _retry_count < HLSSettings.manifestLoadMaxRetry)) {
                CONFIG::LOGGING {
                    Log.warn("I/O Error while trying to load Playlist, retry in " + _retry_timeout + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, _retry_timeout);
                /* exponential increase of retry timeout, capped to manifestLoadMaxRetryTimeout */
                _retry_timeout = Math.min(HLSSettings.manifestLoadMaxRetryTimeout, 2 * _retry_timeout);
                _retry_count++;
                return;
            } else {
                code = HLSError.MANIFEST_LOADING_IO_ERROR;
                txt = "Cannot load M3U8: " + event.text;
            }
            var hlsError : HLSError = new HLSError(code, _url, txt);
            _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
        };

        /** Return the current manifest. **/
        public function get levels() : Vector.<Level> {
            return _levels;
        };

        /** Return the stream type. **/
        public function get type() : String {
            return _type;
        };

        public function get altAudioTracks() : Vector.<AltAudioTrack> {
            return _alt_audio_tracks;
        }

        /** Load the manifest file. **/
        public function load(url : String) : void {
            _close();
            _closed = false;
            _url = url;
            _levels = new Vector.<Level>();
            _canStart = false;
            _reload_playlists_timer = getTimer();
            _retry_timeout = 1000;
            _retry_count = 0;
            _alt_audio_tracks = null;
            _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADING, url));

            if (DataUri.isDataUri(url)) {
                CONFIG::LOGGING {
                    Log.debug("Identified main manifest <" + url + "> as a data URI.");
                }
                var data : String = new DataUri(url).extractData();
                _parseManifest(data || "");
            } else {
                _urlloader.load(new URLRequest(url));
            }
        };

        /** Manifest loaded; check and parse it **/
        private function _loaderHandler(event : Event) : void {
            // successful loading, reset retry counter
            _retry_timeout = 1000;
            _retry_count = 0;
            var loader : URLLoader = URLLoader(event.target);
            _parseManifest(String(loader.data));
        };

        /** parse a playlist **/
        private function _parseLevelPlaylist(string : String, url : String, level : int) : void {
            if (string != null && string.length != 0) {
                CONFIG::LOGGING {
                    Log.debug("level " + level + " playlist:\n" + string);
                }
                var frags : Vector.<Fragment> = Manifest.getFragments(string, url, level);
                // set fragment and update sequence number range
                _levels[level].updateFragments(frags);
                _levels[level].targetduration = Manifest.getTargetDuration(string);
                _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYLIST_DURATION_UPDATED, _levels[level].duration));
            }

            // Check whether the stream is live or not finished yet
            if (Manifest.hasEndlist(string)) {
                _type = HLSTypes.VOD;
                _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_ENDLIST, level));
            } else {
                _type = HLSTypes.LIVE;
                /* in order to determine playlist reload timer,
                    check playback position against playlist duration.
                    if we are near the edge of a live playlist, reload playlist quickly
                    to discover quicker new fragments and avoid buffer starvation.
                */
                var reload_interval : Number = 1000*Math.min((_levels[level].duration - _hls.position)/2,_levels[level].averageduration);
                // avoid spamming the server if we are at the edge ... wait 500ms between 2 reload at least
                var timeout : Number = Math.max(500, _reload_playlists_timer + reload_interval - getTimer());
                CONFIG::LOGGING {
                    Log.debug("Level " + level + " Live Playlist parsing finished: reload in " + timeout.toFixed(0) + " ms");
                }
                _timeoutID = setTimeout(_loadActiveLevelPlaylist, timeout);
            }
            if (!_canStart) {
                _canStart = (_levels[level].fragments.length > 0);
                if (_canStart) {
                    CONFIG::LOGGING {
                        Log.debug("first level filled with at least 1 fragment, notify event");
                    }
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_LOADED, _levels));
                }
            }
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADED, level));
            _manifest_loading = null;
        };

        /** Parse First Level Playlist **/
        private function _parseManifest(string : String) : void {
            // Check for M3U8 playlist or manifest.
            if (string.indexOf(Manifest.HEADER) == 0) {
                // 1 level playlist, create unique level and parse playlist
                if (string.indexOf(Manifest.FRAGMENT) > 0) {
                    var level : Level = new Level();
                    level.url = _url;
                    _levels.push(level);
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, 0));
                    CONFIG::LOGGING {
                        Log.debug("1 Level Playlist, load it");
                    }
                    _current_level = 0;
                    _parseLevelPlaylist(string, _url, 0);
                } else if (string.indexOf(Manifest.LEVEL) > 0) {
                    CONFIG::LOGGING {
                        Log.debug("adaptive playlist:\n" + string);
                    }
                    // adaptative playlist, extract levels from playlist, get them and parse them
                    _levels = Manifest.extractLevels(_hls, string, _url);
                    // retrieve start level from helper function
                    _current_level = _hls.startlevel;
                    _hls.dispatchEvent(new HLSEvent(HLSEvent.MANIFEST_PARSED, _levels));
                    _loadActiveLevelPlaylist();
                    if (string.indexOf(Manifest.ALTERNATE_AUDIO) > 0) {
                        CONFIG::LOGGING {
                            Log.debug("alternate audio level found");
                        }
                        // parse alternate audio tracks
                        _alt_audio_tracks = Manifest.extractAltAudioTracks(string, _url);
                        CONFIG::LOGGING {
                            if (_alt_audio_tracks.length > 0) {
                                Log.debug(_alt_audio_tracks.length + " alternate audio tracks found");
                            }
                        }
                    }
                }
            } else {
                var hlsError : HLSError = new HLSError(HLSError.MANIFEST_PARSING_ERROR, _url, "Manifest is not a valid M3U8 file");
                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
            }
        };

        /** load/reload active M3U8 playlist **/
        private function _loadActiveLevelPlaylist() : void {
            if (_closed) {
                return;
            }
            _reload_playlists_timer = getTimer();
            // load active M3U8 playlist only
            _manifest_loading = new Manifest();
            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_LOADING, _current_level));
            _manifest_loading.loadPlaylist(_levels[_current_level].url, _parseLevelPlaylist, _errorHandler, _current_level, _type, HLSSettings.flushLiveURLCache);
        };

        /** When level switch occurs, assess the need of (re)loading new level playlist **/
        private function _levelSwitchHandler(event : HLSEvent) : void {
            if (_current_level != event.level) {
                _current_level = event.level;
                CONFIG::LOGGING {
                    Log.debug("switch to level " + _current_level);
                }
                if (_type == HLSTypes.LIVE || _levels[_current_level].fragments.length == 0) {
                    _closed = false;
                    CONFIG::LOGGING {
                        Log.debug("(re)load Playlist");
                    }
                    clearTimeout(_timeoutID);
                    _timeoutID = setTimeout(_loadActiveLevelPlaylist, 0);
                }
            }
        };

        private function _close() : void {
            CONFIG::LOGGING {
                Log.debug("cancel any manifest load in progress");
            }
            _closed = true;
            clearTimeout(_timeoutID);
            try {
                _urlloader.close();
                if (_manifest_loading) {
                    _manifest_loading.close();
                }
            } catch(e : Error) {
            }
        }

        /** When the framework idles out, stop reloading manifest **/
        private function _stateHandler(event : HLSEvent) : void {
            if (event.state == HLSPlayStates.IDLE) {
                _close();
            }
        };
    }
}

trim white space

trim white space


/**
  * LzInputTextSprite.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic swf9
  */

/**
  * @shortdesc Used for input text.
  *
  */
public class LzInputTextSprite extends LzTextSprite {

    #passthrough (toplevel:true) {
        import flash.display.InteractiveObject;
        import flash.events.Event;
        import flash.events.FocusEvent;
        import flash.text.TextField;
        import flash.text.TextFieldType;
    }#

    #passthrough  {

    function LzInputTextSprite (newowner:LzView = null, args:Object = null) {
        super(newowner);
    }

    var enabled :Boolean = true;
    var focusable :Boolean = true;
    var hasFocus :Boolean = false;

    override public function __initTextProperties (args:Object) :void {
        super.__initTextProperties(args);
        // We do not support html in input fields.
        this.html = false;

        if (this.enabled) {
            textfield.type = TextFieldType.INPUT;
        } else {
            textfield.type = TextFieldType.DYNAMIC;
        }

        /*
          TODO [hqm 2008-01]
          these handlers need to be implemented via Flash native event listenters:

          focusIn , focusOut

          change -- dispatched after text input is modified

          textInput  -- dispatched before the content is modified
                        Do we need to use this for intercepting when we have
                        a pattern restriction on input?

        */

        textfield.addEventListener(Event.CHANGE        , __onChanged);
        //textfield.addEventListener(TextEvent.TEXT_INPUT, __onTextInput);
        textfield.addEventListener(FocusEvent.FOCUS_IN , __gotFocus);
        textfield.addEventListener(FocusEvent.FOCUS_OUT, __lostFocus);

        this.hasFocus = false;
    }

    /**
     * Called from LzInputText#_gotFocusEvent() after focus was set by lz.Focus
     * @access private
     */
    public function gotFocus () :void {
        if ( this.hasFocus ) { return; }
        // assign keyboard control
        LFCApplication.stage.focus = this.textfield;
        this.select();
        this.hasFocus = true;
    }

    /**
     * Called from LzInputText#_gotBlurEvent() after focus was cleared by lz.Focus
     * @access private
     */
    function gotBlur () :void {
        this.hasFocus = false;
        this.deselect();
        if (LFCApplication.stage.focus === this.textfield) {
            // remove keyboard control
            LFCApplication.stage.focus = LFCApplication.stage;
        }
    }

    function select () :void {
        textfield.setSelection(0, textfield.text.length);
    }

    function deselect () :void {
        textfield.setSelection(0, 0);
    }

    /**
     * TODO [hqm 2008-01] I have no idea whether this comment
     * still applies:
     * Register for update on every frame when the text field gets the focus.
     * Set the behavior of the enter key depending on whether the field is
     * multiline or not.
     *
     * @access private
     */
    function __gotFocus (event:FocusEvent) :void {
        // scroll text fields horizontally back to start
        if (owner) owner.inputtextevent('onfocus');
    }

    /**
     * @access private
     * TODO [hqm 2008-01] Does we still need this workaround???
     */
    function __lostFocus (event:FocusEvent) :void {
        // defer execution, see swf8 kernel
        LzTimeKernel.setTimeout(this.__handlelostFocus, 1, event);
    }

    /**
     * TODO [hqm 2008-01] Does this comment still apply? Do we still need this workaround???
     *
     * must be called after an idle event to prevent the selection from being
     * cleared prematurely, e.g. before a button click.  If the selection is
     * cleared, the button doesn't send mouse events.
     * @access private
     */
    function __handlelostFocus (event:Event) :void {
        if (owner) owner.inputtextevent('onblur');
    }

    /**
     * Register to be called when the text field is modified. Convert this
     * into a LFC ontext event.
     * @access private
     */
    function __onChanged (event:Event) :void {
        this.text = this.getText();
        if (owner) owner.inputtextevent('onchange', this.text);
    }

    /**
     * Get the current text for this inputtext-sprite.
     * @protected
     */
    override public function getText() :String {
        // We normalize swf's \r to \n
        return this.textfield.text.replace(/\r/g, '\n');
    }

    /**
     * Sets whether user can modify input text field
     * @param Boolean enabled: true if the text field can be edited
     */
    function setEnabled (enabled:Boolean) :void {
        this.enabled = enabled;
        if (enabled) {
            textfield.type = 'input';
        } else {
            textfield.type = 'dynamic';
        }
    }

    /**
     * If a mouse event occurs in an input text field, find the focused view
     */
    static function findSelection() :LzInputText {
        var f:InteractiveObject = LFCApplication.stage.focus;
        if (f is TextField && f.parent is LzInputTextSprite) {
            return (f.parent as LzInputTextSprite).owner;
        }
        return null;
    }

    }#  // #passthrough

} // End of LzInputTextSprite

/**
  * LzInputTextSprite.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic swf9
  */

/**
  * @shortdesc Used for input text.
  *
  */
public class LzInputTextSprite extends LzTextSprite {

    #passthrough (toplevel:true) {
        import flash.display.InteractiveObject;
        import flash.events.Event;
        import flash.events.FocusEvent;
        import flash.text.TextField;
        import flash.text.TextFormat;
        import flash.text.TextFieldType;
    }#

    #passthrough  {

    function LzInputTextSprite (newowner:LzView = null, args:Object = null) {
        super(newowner);
    }

    var enabled :Boolean = true;
    var focusable :Boolean = true;
    var hasFocus :Boolean = false;

    override public function __initTextProperties (args:Object) :void {
        super.__initTextProperties(args);
        // We do not support html in input fields.
        this.html = false;

        if (this.enabled) {
            textfield.type = TextFieldType.INPUT;
        } else {
            textfield.type = TextFieldType.DYNAMIC;
        }

        /*
          TODO [hqm 2008-01]
          these handlers need to be implemented via Flash native event listenters:

          focusIn , focusOut

          change -- dispatched after text input is modified

          textInput  -- dispatched before the content is modified
                        Do we need to use this for intercepting when we have
                        a pattern restriction on input?

        */

        textfield.addEventListener(Event.CHANGE        , __onChanged);
        //textfield.addEventListener(TextEvent.TEXT_INPUT, __onTextInput);
        textfield.addEventListener(FocusEvent.FOCUS_IN , __gotFocus);
        textfield.addEventListener(FocusEvent.FOCUS_OUT, __lostFocus);

        this.hasFocus = false;
    }

    /**
     * Called from LzInputText#_gotFocusEvent() after focus was set by lz.Focus
     * @access private
     */
    public function gotFocus () :void {
        if ( this.hasFocus ) { return; }
        // assign keyboard control
        LFCApplication.stage.focus = this.textfield;
        this.select();
        this.hasFocus = true;
    }

    /**
     * Called from LzInputText#_gotBlurEvent() after focus was cleared by lz.Focus
     * @access private
     */
    function gotBlur () :void {
        this.hasFocus = false;
        this.deselect();
        if (LFCApplication.stage.focus === this.textfield) {
            // remove keyboard control
            LFCApplication.stage.focus = LFCApplication.stage;
        }
    }

    function select () :void {
        textfield.setSelection(0, textfield.text.length);
    }

    function deselect () :void {
        textfield.setSelection(0, 0);
    }

    /**
     * TODO [hqm 2008-01] I have no idea whether this comment
     * still applies:
     * Register for update on every frame when the text field gets the focus.
     * Set the behavior of the enter key depending on whether the field is
     * multiline or not.
     *
     * @access private
     */
    function __gotFocus (event:FocusEvent) :void {
        // scroll text fields horizontally back to start
        if (owner) owner.inputtextevent('onfocus');
    }

    /**
     * @access private
     * TODO [hqm 2008-01] Does we still need this workaround???
     */
    function __lostFocus (event:FocusEvent) :void {
        // defer execution, see swf8 kernel
        LzTimeKernel.setTimeout(this.__handlelostFocus, 1, event);
    }

    /**
     * TODO [hqm 2008-01] Does this comment still apply? Do we still need this workaround???
     *
     * must be called after an idle event to prevent the selection from being
     * cleared prematurely, e.g. before a button click.  If the selection is
     * cleared, the button doesn't send mouse events.
     * @access private
     */
    function __handlelostFocus (event:Event) :void {
        if (owner) owner.inputtextevent('onblur');
    }

    /**
     * Register to be called when the text field is modified. Convert this
     * into a LFC ontext event.
     * @access private
     */
    function __onChanged (event:Event) :void {
        this.text = this.getText();
        if (owner) owner.inputtextevent('onchange', this.text);
    }

    /**
     * Get the current text for this inputtext-sprite.
     * @protected
     */
    override public function getText() :String {
        // We normalize swf's \r to \n
        return this.textfield.text.replace(/\r/g, '\n');
    }

    /**
     * Sets whether user can modify input text field
     * @param Boolean enabled: true if the text field can be edited
     */
    function setEnabled (enabled:Boolean) :void {
        this.enabled = enabled;
        if (enabled) {
            textfield.type = TextFieldType.INPUT;
        } else {
            textfield.type = TextFieldType.DYNAMIC;
        }

        var df:TextFormat = textfield.defaultTextFormat;
        // reset textformat to workaround flash player bug (FP-77)
        textfield.defaultTextFormat = df;
    }

    /**
     * If a mouse event occurs in an input text field, find the focused view
     */
    static function findSelection() :LzInputText {
        var f:InteractiveObject = LFCApplication.stage.focus;
        if (f is TextField && f.parent is LzInputTextSprite) {
            return (f.parent as LzInputTextSprite).owner;
        }
        return null;
    }

    }#  // #passthrough

} // End of LzInputTextSprite

Change 20091028-hqm-2 by hqm@badtzmaru.home on 2009-10-28 15:53:59 EDT
    in /Users/hqm/openlaszlo/trunk-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary:  fix bug with disable/enable input text in swf10

New Features:

Bugs Fixed: LPP-8574

Technical Reviewer: andre
QA Reviewer: max
Doc Reviewer: (pending)

Documentation:

Release Notes:

Details:

Use the workaround suggested by Flash Player bug FP-77, reset the defaultTextFormat manually.

Tests:

test case from bug report works

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@15073 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


package laml.display {
	import flash.display.DisplayObject;
	import flash.display.Sprite;

	public class Skin extends Sprite implements ISkin {

		public function getBitmapByName(alias:String):DisplayObject {
			if(hasOwnProperty(alias)) {
				return new this[alias]() as DisplayObject;
			}

			return null;
		}
	}
}

package laml.display {
	import flash.display.DisplayObject;
	import flash.display.Sprite;

	import mx.core.BitmapAsset;
	import mx.core.FontAsset;
	import mx.core.IFlexAsset;
	import mx.core.IFlexDisplayObject;
	import mx.core.SpriteAsset;

	public class Skin extends Sprite implements ISkin {
 		private var bitmapAsset:BitmapAsset;
		private var fontAsset:FontAsset;
		private var iFlexAsset:IFlexAsset;
		private var iFlexDisplayObject:IFlexDisplayObject;
		private var spriteAsset:SpriteAsset;

		public function getBitmapByName(alias:String):DisplayObject {
			if(hasOwnProperty(alias)) {
				return new this[alias]() as DisplayObject;
			}

			return null;
		}
	}
}

Put MX dependencies in skin

Put MX dependencies in skin

git-svn-id: 02605e11d3a461bc7e12f3f3880edf4b0c8dcfd0@4281 3e7533ad-8678-4d30-8e38-00a379d3f0d0


package com.dotfold.dotvimstat.view.summary
{
	import com.dotfold.dotvimstat.view.IView;

	import flash.display.Graphics;
	import flash.display.Shape;

	import modena.core.Element;
	import modena.core.ElementContent;
	import modena.ui.Stack;

	/**
	 *
	 * @author jamesmcnamee
	 *
	 */
	public class LikesSummary extends Stack implements IView
	{
		public static const ELEMENT_NAME:String = "likesSummary";

		/**
		 * Constructor.
		 */
		public function LikesSummary(styleClass:String = null)
		{
			super(styleClass);
		}

		override protected function createElementContent():ElementContent
		{
			var content:ElementContent = super.createElementContent();

			return content;
		}

		override public function validateRender():void
		{
			super.validateRender();
		}
	}
}

package com.dotfold.dotvimstat.view.summary
{
	import com.dotfold.dotvimstat.view.IView;

	import flash.display.Graphics;
	import flash.display.Shape;

	import modena.core.Element;
	import modena.core.ElementContent;
	import modena.ui.Label;
	import modena.ui.Stack;

	/**
	 *
	 * @author jamesmcnamee
	 *
	 */
	public class LikesSummary extends Stack implements IView
	{
		public static const ELEMENT_NAME:String = "likesSummary";

		private var _label:Label;

		/**
		 * Constructor.
		 */
		public function LikesSummary(styleClass:String = null)
		{
			super(styleClass);
		}

		override protected function createElementContent():ElementContent
		{
			var content:ElementContent = super.createElementContent();

			_label = new Label("summaryCount");
			content.addChild(_label)

			return content;
		}

		/**
		 *
		 */
		public function set likesCount(value:int):void
		{
			_label.text = value.toString();
			invalidateRender();
		}

		override public function validateRender():void
		{
			super.validateRender();

			_label.x = (width - _label.width) / 2;
			_label.y = (height - _label.height) / 2;
		}
	}
}

add label to likes summary

[feat] add label to likes summary


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

package com.threerings.flex {

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;

import flash.events.Event;
import flash.events.IEventDispatcher;

import flash.geom.Point;
import flash.geom.Rectangle;

import mx.controls.Menu;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.menuClasses.IMenuItemRenderer;
import mx.controls.menuClasses.MenuListData;

import mx.core.mx_internal;
import mx.core.Application;
import mx.core.ClassFactory;
import mx.core.IFlexDisplayObject;
import mx.core.ScrollPolicy;

import mx.events.MenuEvent;

import mx.managers.PopUpManager;

import com.threerings.util.CommandEvent;

import com.threerings.flex.PopUpUtil;

import com.threerings.flex.menuClasses.CommandMenuItemRenderer;
import com.threerings.flex.menuClasses.CommandListData;

use namespace mx_internal;

/**
 * A pretty standard menu that can submit CommandEvents if menu items have "command" and possibly
 * "arg" properties. Commands are submitted to controllers for processing. Alternatively, you may
 * specify "callback" properties that specify a function closure to call, with the "arg" property
 * containing either a single arg or an array of args.
 *
 * Another property now supported is "iconObject", which is an already-instantiated
 * IFlexDisplayObject to use as the icon. This will only be applied if the "icon" property
 * (which specifies a Class) is not used.
 *
 * Example dataProvider array:
 * [ { label: "Go home", icon: homeIconClass,
 *     command: Controller.GO_HOME, arg: homeId },
 *   { type: "separator"},
     { label: "Crazytown", callback: setCrazy, arg: [ true, false ] },
 *   { label: "Other places", children: subMenuArray }
 * ];
 *
 * See "Defining menu structure and data" in the Flex manual for the full list.
 *
 * CommandMenu is scrollable.  This can be forced by setting verticalScrollPolicy to
 * ScrollPolicy.AUTO or ScrollPolicy.ON and setting the maxHeight.  Also, if the scrolling isn't
 * forced on, but the menu does not fit within the vertical bounds given (either the stage size by
 * default, or the height of the rectangle given in setBounds()), scrolling will be turned on
 * so that none of the content is lost.
 *
 * Note: we don't extend flexlib's ScrollableMenu (or its sub-class ScrollableArrowMenu) because it
 * does some weird stuff in measure() that forces some of our menus down to a very small height...
 */
public class CommandMenu extends Menu
{
    /**
     * Factory method to create a command menu.
     *
     * @param items an array of menu items.
     * @param dispatcher an override event dispatcher to use for command events, rather than
     * our parent.
     */
    public static function createMenu (
        items :Array, dispatcher :IEventDispatcher = null) :CommandMenu
    {
        var menu :CommandMenu = new CommandMenu();
        menu.owner = DisplayObjectContainer(Application.application);
        menu.tabEnabled = false;
        menu.showRoot = true;
        menu.setDispatcher(dispatcher);
        Menu.popUpMenu(menu, null, items); // does not actually pop up, but needed.
        return menu;
    }

    /**
     * The mx.controls.Menu class overrides setting and getting the verticalScrollPolicy
     * basically setting the verticalScrollPolicy did nothing, and getting it always returned
     * ScrollPolicy.OFF.  So that's not going to work if we want the menu to scroll. Here we
     * reinstate the verticalScrollPolicy setter, and keep a local copy of the value in a
     * protected variable _verticalScrollPolicy.
     *
     * This setter is basically a copy of what ScrollControlBase and ListBase do.
     */
    override public function set verticalScrollPolicy (value :String) :void
    {
        var newPolicy :String = value.toLowerCase();

        itemsSizeChanged = true;

        if (_verticalScrollPolicy != newPolicy)
        {
            _verticalScrollPolicy = newPolicy;
            dispatchEvent(new Event("verticalScrollPolicyChanged"));
        }

        invalidateDisplayList();
    }

    override public function get verticalScrollPolicy () :String
    {
        return _verticalScrollPolicy;
    }

    public function CommandMenu ()
    {
        super();

        itemRenderer = new ClassFactory(CommandMenuItemRenderer);

        verticalScrollPolicy = ScrollPolicy.OFF;
        addEventListener(MenuEvent.ITEM_CLICK, itemClicked);
    }

    /**
     * Configures the event dispatcher to be used when dispatching this menu's command events.
     * Normally they will be dispatched on our parent (usually the SystemManager or something).
     */
    public function setDispatcher (dispatcher :IEventDispatcher) :void
    {
        _dispatcher = dispatcher;
    }

    /**
     * Sets a Rectangle (in stage coords) that the menu will attempt to keep submenus positioned
     * within.  If a submenu is too large to fit, it will position it in the lower right corner
     * of this Rectangle (or top if popping upwards, or left if popping leftwards).   This
     * value defaults to the stage bounds.
     */
    public function setBounds (fitting :Rectangle) :void
    {
        _fitting = fitting;
    }

    /**
     * Actually pop up the menu. This can be used instead of show().
     */
    public function popUp (
        trigger :DisplayObject, popUpwards :Boolean = false, popLeftwards :Boolean = false) :void
    {
        _upping = popUpwards;
        _lefting = popLeftwards;
        var r :Rectangle = trigger.getBounds(trigger.stage);
        show(_lefting ? r.left : r.right, _upping ? r.top : r.bottom);
    }

    /**
     * Shows the menu at the specified mouse coordinates.
     */
    public function popUpAt (
        mx :int, my :int, popUpwards :Boolean = false, popLeftwards :Boolean = false) :void
    {
        _upping = popUpwards;
        _lefting = popLeftwards;
        show(mx, my);
    }

    /**
     * Shows the menu at the current mouse location.
     */
    public function popUpAtMouse (popUpwards :Boolean = false, popLeftwards :Boolean = false) :void
    {
        _upping = popUpwards;
        _lefting = popLeftwards;
        show(); // our show, with no args, pops at the mouse
    }

    /**
     * Just like our superclass's show(), except that when invoked with no args, causes the menu to
     * show at the current mouse location instead of the top-left corner of the application.
     * Also, we ensure that the resulting menu is in-bounds.
     */
    override public function show (xShow :Object = null, yShow :Object = null) :void
    {
        if (xShow == null) {
            xShow = DisplayObject(Application.application).mouseX;
        }
        if (yShow == null) {
            yShow = DisplayObject(Application.application).mouseY;
        }

        super.show(xShow, yShow);

        // reposition now that we know our size
        if (_lefting) {
            y = x - getExplicitOrMeasuredWidth();
        }
        if (_upping) {
            y = y - getExplicitOrMeasuredHeight();
        }

        var fitting :Rectangle = _fitting || screen;
        PopUpUtil.fitInRect(this, fitting);

        // if after fitting as best we can, the menu is outside of the declared bounds, we force
        // scrolling and set the max height
        if (y < fitting.y) {
            verticalScrollPolicy = ScrollPolicy.AUTO;
            maxHeight = fitting.height;
            y = fitting.y;
        }
    }

    /**
     * The Menu class overrode configureScrollBars() and made the function do nothing.  That means
     * the scrollbars don't know how to draw themselves, so here we reinstate configureScrollBars.
     * This is basically a copy of the same method from the mx.controls.List class.
     */
    override protected function configureScrollBars () :void
    {
        var rowCount :int = listItems.length;
        if (rowCount == 0) {
            return;
        }

        // if there is more than one row and it is a partial row we don't count it
        if (rowCount > 1 &&
            rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height > listContent.height) {
            rowCount--;
        }

        // offset, when added to rowCount, is hte index of the dataProvider item for that row.
        // IOW, row 10 in listItems is showing dataProvider item 10 + verticalScrollPosition -
        // lockedRowCount - 1
        var offset :int = verticalScrollPosition - lockedRowCount - 1;

        // don't count filler rows at the bottom either.
        var fillerRows :int = 0;
        while (rowCount > 0 && listItems[rowCount - 1].length == 0)
        {
            if (collection && rowCount + offset >= collection.length) {
                rowCount--;
                fillerRows++;
            } else {
                break;
            }
        }

        var colCount :int = listItems[0].length;
        var oldHorizontalScrollBar :Object = horizontalScrollBar;
        var oldVerticalScrollBar :Object = verticalScrollBar;
        var roundedWidth :int = Math.round(unscaledWidth);
        var length :int = collection ? collection.length - lockedRowCount : 0;
        var numRows :int = rowCount - lockedRowCount;

        setScrollBarProperties(Math.round(listContent.width), roundedWidth, length, numRows);
        maxVerticalScrollPosition = Math.max(length - numRows, 0);
    }

    /**
     * Callback for MenuEvent.ITEM_CLICK.
     */
    protected function itemClicked (event :MenuEvent) :void
    {
        var arg :Object = getItemProp(event.item, "arg");
        var cmdOrFn :Object = getItemProp(event.item, "command");
        if (cmdOrFn == null) {
            cmdOrFn = getItemProp(event.item, "callback");
        }
        if (cmdOrFn != null) {
            event.stopImmediatePropagation();
            CommandEvent.dispatch(_dispatcher == null ? mx_internal::parentDisplayObject :
                                  _dispatcher, cmdOrFn, arg);
        }
        // else: no warning. There may be non-command menu items mixed in.
    }

    // from ScrollableArrowMenu..
    override mx_internal function openSubMenu (row :IListItemRenderer) :void
    {
        supposedToLoseFocus = true;

        var r :Menu = getRootMenu();
        var menu :CommandMenu;

        // check to see if the menu exists, if not create it
        if (!IMenuItemRenderer(row).menu) {
            // the only differences between this method and the original method in mx.controls.Menu
            // are these few lines.
            menu = new CommandMenu();
            menu.maxHeight = this.maxHeight;
            menu.verticalScrollPolicy = this.verticalScrollPolicy;
            menu.variableRowHeight = this.variableRowHeight;

            menu.parentMenu = this;
            menu.owner = this;
            menu.showRoot = showRoot;
            menu.dataDescriptor = r.dataDescriptor;
            menu.styleName = r;
            menu.labelField = r.labelField;
            menu.labelFunction = r.labelFunction;
            menu.iconField = r.iconField;
            menu.iconFunction = r.iconFunction;
            menu.itemRenderer = r.itemRenderer;
            menu.rowHeight = r.rowHeight;
            menu.scaleY = r.scaleY;
            menu.scaleX = r.scaleX;

            // if there's data and it has children then add the items
            if (row.data && _dataDescriptor.isBranch(row.data) &&
                    _dataDescriptor.hasChildren(row.data)) {
                menu.dataProvider = _dataDescriptor.getChildren(row.data);
            }
            menu.sourceMenuBar = sourceMenuBar;
            menu.sourceMenuBarItem = sourceMenuBarItem;

            IMenuItemRenderer(row).menu = menu;
            PopUpManager.addPopUp(menu, r, false);
        }

        super.openSubMenu(row);

        // if we're lefting, upping or fitting make sure our submenu does so as well
        var submenu :Menu = IMenuItemRenderer(row).menu;
        if (_lefting) {
            submenu.x -= submenu.getExplicitOrMeasuredWidth();
        }
        if (_upping) {
            var displayObj :DisplayObject = DisplayObject(row);
            var rowLoc :Point = displayObj.localToGlobal(new Point(row.x, row.y));
            submenu.y = rowLoc.y - submenu.getExplicitOrMeasuredHeight() + displayObj.height;
        }

        var fitting :Rectangle = _fitting || screen;
        PopUpUtil.fitInRect(submenu, fitting);
        // if after fitting as best we can, the menu is outside of the declared bounds, we force
        // scrolling and set the max height
        if (submenu.y < fitting.y) {
            submenu.verticalScrollPolicy = ScrollPolicy.AUTO;
            submenu.maxHeight = fitting.height;
            submenu.y = fitting.y;
        }
    }

    override protected function measure () :void
    {
        super.measure();

        if (measuredHeight > this.maxHeight) {
            measuredHeight = this.maxHeight;
        }

        if (verticalScrollPolicy == ScrollPolicy.ON || verticalScrollPolicy == ScrollPolicy.AUTO) {
            if (verticalScrollBar) {
                measuredMinWidth = measuredWidth = measuredWidth + verticalScrollBar.minWidth;
            }
        }

        commitProperties();
    }

    // from List
    override protected function makeListData (data :Object, uid :String, rowNum :int) :BaseListData
    {
        // Oh, FFS.
        // We need to set up these "maxMeasuredIconWidth" fields on the MenuListData, but our
        // superclass has made those variables private.
        // We can get the values out of another MenuListData, so we just always call super()
        // to create one of those, and if we need to make a CommandListData, we construct one
        // from the fields in the MenuListData.

        var menuListData :MenuListData = super.makeListData(data, uid, rowNum) as MenuListData;

        var iconObject :IFlexDisplayObject = getItemProp(data, "iconObject") as IFlexDisplayObject;
        if (iconObject != null) {
            var cmdListData :CommandListData = new CommandListData(menuListData.label,
                menuListData.icon, iconObject, labelField, uid, this, rowNum);

            cmdListData.maxMeasuredIconWidth = menuListData.maxMeasuredIconWidth;
            cmdListData.maxMeasuredTypeIconWidth = menuListData.maxMeasuredTypeIconWidth;
            cmdListData.maxMeasuredBranchIconWidth = menuListData.maxMeasuredBranchIconWidth;
            cmdListData.useTwoColumns = menuListData.useTwoColumns;
            return cmdListData;
        }

        return menuListData;
    }

    /**
     * Get the specified property for the specified item, if any.  Somewhat similar to bits in the
     * DefaultDataDescriptor.
     */
    protected function getItemProp (item :Object, prop :String) :Object
    {
        try {
            if (item is XML) {
                return String((item as XML).attribute(prop));

            } else if (prop in item) {
                return item[prop];
            }

        } catch (e :Error) {
            // alas; fall through
        }
        return null;
    }

    protected var _dispatcher :IEventDispatcher;
    protected var _lefting :Boolean = false;
    protected var _upping :Boolean = false;
    protected var _fitting :Rectangle = null;
    protected var _verticalScrollPolicy :String;
}
}

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

package com.threerings.flex {

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;

import flash.events.Event;
import flash.events.IEventDispatcher;

import flash.geom.Point;
import flash.geom.Rectangle;

import mx.controls.Menu;
import mx.controls.listClasses.BaseListData;
import mx.controls.listClasses.IListItemRenderer;
import mx.controls.menuClasses.IMenuItemRenderer;
import mx.controls.menuClasses.MenuListData;

import mx.core.mx_internal;
import mx.core.Application;
import mx.core.ClassFactory;
import mx.core.IFlexDisplayObject;
import mx.core.ScrollPolicy;

import mx.events.MenuEvent;

import mx.managers.PopUpManager;

import com.threerings.util.CommandEvent;

import com.threerings.flex.PopUpUtil;

import com.threerings.flex.menuClasses.CommandMenuItemRenderer;
import com.threerings.flex.menuClasses.CommandListData;

use namespace mx_internal;

/**
 * A pretty standard menu that can submit CommandEvents if menu items have "command" and possibly
 * "arg" properties. Commands are submitted to controllers for processing. Alternatively, you may
 * specify "callback" properties that specify a function closure to call, with the "arg" property
 * containing either a single arg or an array of args.
 *
 * Another property now supported is "iconObject", which is an already-instantiated
 * IFlexDisplayObject to use as the icon. This will only be applied if the "icon" property
 * (which specifies a Class) is not used.
 *
 * Example dataProvider array:
 * [ { label: "Go home", icon: homeIconClass,
 *     command: Controller.GO_HOME, arg: homeId },
 *   { type: "separator"},
     { label: "Crazytown", callback: setCrazy, arg: [ true, false ] },
 *   { label: "Other places", children: subMenuArray }
 * ];
 *
 * See "Defining menu structure and data" in the Flex manual for the full list.
 *
 * CommandMenu is scrollable.  This can be forced by setting verticalScrollPolicy to
 * ScrollPolicy.AUTO or ScrollPolicy.ON and setting the maxHeight.  Also, if the scrolling isn't
 * forced on, but the menu does not fit within the vertical bounds given (either the stage size by
 * default, or the height of the rectangle given in setBounds()), scrolling will be turned on
 * so that none of the content is lost.
 *
 * Note: we don't extend flexlib's ScrollableMenu (or its sub-class ScrollableArrowMenu) because it
 * does some weird stuff in measure() that forces some of our menus down to a very small height...
 */
public class CommandMenu extends Menu
{
    /**
     * Factory method to create a command menu.
     *
     * @param items an array of menu items.
     * @param dispatcher an override event dispatcher to use for command events, rather than
     * our parent.
     */
    public static function createMenu (
        items :Array, dispatcher :IEventDispatcher = null) :CommandMenu
    {
        var menu :CommandMenu = new CommandMenu();
        menu.owner = DisplayObjectContainer(Application.application);
        menu.tabEnabled = false;
        menu.showRoot = true;
        menu.setDispatcher(dispatcher);
        Menu.popUpMenu(menu, null, items); // does not actually pop up, but needed.
        return menu;
    }

    /**
     * The mx.controls.Menu class overrides setting and getting the verticalScrollPolicy
     * basically setting the verticalScrollPolicy did nothing, and getting it always returned
     * ScrollPolicy.OFF.  So that's not going to work if we want the menu to scroll. Here we
     * reinstate the verticalScrollPolicy setter, and keep a local copy of the value in a
     * protected variable _verticalScrollPolicy.
     *
     * This setter is basically a copy of what ScrollControlBase and ListBase do.
     */
    override public function set verticalScrollPolicy (value :String) :void
    {
        var newPolicy :String = value.toLowerCase();

        itemsSizeChanged = true;

        if (_verticalScrollPolicy != newPolicy)
        {
            _verticalScrollPolicy = newPolicy;
            dispatchEvent(new Event("verticalScrollPolicyChanged"));
        }

        invalidateDisplayList();
    }

    override public function get verticalScrollPolicy () :String
    {
        return _verticalScrollPolicy;
    }

    public function CommandMenu ()
    {
        super();

        itemRenderer = new ClassFactory(getItemRenderer());

        verticalScrollPolicy = ScrollPolicy.OFF;
        addEventListener(MenuEvent.ITEM_CLICK, itemClicked);
    }

    /**
     * Called in the CommandMenu constructor, this should return the item rendering class for this
     * CommandMenu.
     */
    protected function getItemRenderer () :Class
    {
        return CommandMenuItemRenderer;
    }

    /**
     * Configures the event dispatcher to be used when dispatching this menu's command events.
     * Normally they will be dispatched on our parent (usually the SystemManager or something).
     */
    public function setDispatcher (dispatcher :IEventDispatcher) :void
    {
        _dispatcher = dispatcher;
    }

    /**
     * Sets a Rectangle (in stage coords) that the menu will attempt to keep submenus positioned
     * within.  If a submenu is too large to fit, it will position it in the lower right corner
     * of this Rectangle (or top if popping upwards, or left if popping leftwards).   This
     * value defaults to the stage bounds.
     */
    public function setBounds (fitting :Rectangle) :void
    {
        _fitting = fitting;
    }

    /**
     * Actually pop up the menu. This can be used instead of show().
     */
    public function popUp (
        trigger :DisplayObject, popUpwards :Boolean = false, popLeftwards :Boolean = false) :void
    {
        _upping = popUpwards;
        _lefting = popLeftwards;
        var r :Rectangle = trigger.getBounds(trigger.stage);
        show(_lefting ? r.left : r.right, _upping ? r.top : r.bottom);
    }

    /**
     * Shows the menu at the specified mouse coordinates.
     */
    public function popUpAt (
        mx :int, my :int, popUpwards :Boolean = false, popLeftwards :Boolean = false) :void
    {
        _upping = popUpwards;
        _lefting = popLeftwards;
        show(mx, my);
    }

    /**
     * Shows the menu at the current mouse location.
     */
    public function popUpAtMouse (popUpwards :Boolean = false, popLeftwards :Boolean = false) :void
    {
        _upping = popUpwards;
        _lefting = popLeftwards;
        show(); // our show, with no args, pops at the mouse
    }

    /**
     * Just like our superclass's show(), except that when invoked with no args, causes the menu to
     * show at the current mouse location instead of the top-left corner of the application.
     * Also, we ensure that the resulting menu is in-bounds.
     */
    override public function show (xShow :Object = null, yShow :Object = null) :void
    {
        if (xShow == null) {
            xShow = DisplayObject(Application.application).mouseX;
        }
        if (yShow == null) {
            yShow = DisplayObject(Application.application).mouseY;
        }

        super.show(xShow, yShow);

        // reposition now that we know our size
        if (_lefting) {
            y = x - getExplicitOrMeasuredWidth();
        }
        if (_upping) {
            y = y - getExplicitOrMeasuredHeight();
        }

        var fitting :Rectangle = _fitting || screen;
        PopUpUtil.fitInRect(this, fitting);

        // if after fitting as best we can, the menu is outside of the declared bounds, we force
        // scrolling and set the max height
        if (y < fitting.y) {
            verticalScrollPolicy = ScrollPolicy.AUTO;
            maxHeight = fitting.height;
            y = fitting.y;
        }
    }

    /**
     * The Menu class overrode configureScrollBars() and made the function do nothing.  That means
     * the scrollbars don't know how to draw themselves, so here we reinstate configureScrollBars.
     * This is basically a copy of the same method from the mx.controls.List class.
     */
    override protected function configureScrollBars () :void
    {
        var rowCount :int = listItems.length;
        if (rowCount == 0) {
            return;
        }

        // if there is more than one row and it is a partial row we don't count it
        if (rowCount > 1 &&
            rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height > listContent.height) {
            rowCount--;
        }

        // offset, when added to rowCount, is hte index of the dataProvider item for that row.
        // IOW, row 10 in listItems is showing dataProvider item 10 + verticalScrollPosition -
        // lockedRowCount - 1
        var offset :int = verticalScrollPosition - lockedRowCount - 1;

        // don't count filler rows at the bottom either.
        var fillerRows :int = 0;
        while (rowCount > 0 && listItems[rowCount - 1].length == 0)
        {
            if (collection && rowCount + offset >= collection.length) {
                rowCount--;
                fillerRows++;
            } else {
                break;
            }
        }

        var colCount :int = listItems[0].length;
        var oldHorizontalScrollBar :Object = horizontalScrollBar;
        var oldVerticalScrollBar :Object = verticalScrollBar;
        var roundedWidth :int = Math.round(unscaledWidth);
        var length :int = collection ? collection.length - lockedRowCount : 0;
        var numRows :int = rowCount - lockedRowCount;

        setScrollBarProperties(Math.round(listContent.width), roundedWidth, length, numRows);
        maxVerticalScrollPosition = Math.max(length - numRows, 0);
    }

    /**
     * Callback for MenuEvent.ITEM_CLICK.
     */
    protected function itemClicked (event :MenuEvent) :void
    {
        var arg :Object = getItemProp(event.item, "arg");
        var cmdOrFn :Object = getItemProp(event.item, "command");
        if (cmdOrFn == null) {
            cmdOrFn = getItemProp(event.item, "callback");
        }
        if (cmdOrFn != null) {
            event.stopImmediatePropagation();
            CommandEvent.dispatch(_dispatcher == null ? mx_internal::parentDisplayObject :
                                  _dispatcher, cmdOrFn, arg);
        }
        // else: no warning. There may be non-command menu items mixed in.
    }

    // from ScrollableArrowMenu..
    override mx_internal function openSubMenu (row :IListItemRenderer) :void
    {
        supposedToLoseFocus = true;

        var r :Menu = getRootMenu();
        var menu :CommandMenu;

        // check to see if the menu exists, if not create it
        if (!IMenuItemRenderer(row).menu) {
            // the only differences between this method and the original method in mx.controls.Menu
            // are these few lines.
            menu = new CommandMenu();
            menu.maxHeight = this.maxHeight;
            menu.verticalScrollPolicy = this.verticalScrollPolicy;
            menu.variableRowHeight = this.variableRowHeight;

            menu.parentMenu = this;
            menu.owner = this;
            menu.showRoot = showRoot;
            menu.dataDescriptor = r.dataDescriptor;
            menu.styleName = r;
            menu.labelField = r.labelField;
            menu.labelFunction = r.labelFunction;
            menu.iconField = r.iconField;
            menu.iconFunction = r.iconFunction;
            menu.itemRenderer = r.itemRenderer;
            menu.rowHeight = r.rowHeight;
            menu.scaleY = r.scaleY;
            menu.scaleX = r.scaleX;

            // if there's data and it has children then add the items
            if (row.data && _dataDescriptor.isBranch(row.data) &&
                    _dataDescriptor.hasChildren(row.data)) {
                menu.dataProvider = _dataDescriptor.getChildren(row.data);
            }
            menu.sourceMenuBar = sourceMenuBar;
            menu.sourceMenuBarItem = sourceMenuBarItem;

            IMenuItemRenderer(row).menu = menu;
            PopUpManager.addPopUp(menu, r, false);
        }

        super.openSubMenu(row);

        // if we're lefting, upping or fitting make sure our submenu does so as well
        var submenu :Menu = IMenuItemRenderer(row).menu;
        if (_lefting) {
            submenu.x -= submenu.getExplicitOrMeasuredWidth();
        }
        if (_upping) {
            var displayObj :DisplayObject = DisplayObject(row);
            var rowLoc :Point = displayObj.localToGlobal(new Point(row.x, row.y));
            submenu.y = rowLoc.y - submenu.getExplicitOrMeasuredHeight() + displayObj.height;
        }

        var fitting :Rectangle = _fitting || screen;
        PopUpUtil.fitInRect(submenu, fitting);
        // if after fitting as best we can, the menu is outside of the declared bounds, we force
        // scrolling and set the max height
        if (submenu.y < fitting.y) {
            submenu.verticalScrollPolicy = ScrollPolicy.AUTO;
            submenu.maxHeight = fitting.height;
            submenu.y = fitting.y;
        }
    }

    override protected function measure () :void
    {
        super.measure();

        if (measuredHeight > this.maxHeight) {
            measuredHeight = this.maxHeight;
        }

        if (verticalScrollPolicy == ScrollPolicy.ON || verticalScrollPolicy == ScrollPolicy.AUTO) {
            if (verticalScrollBar) {
                measuredMinWidth = measuredWidth = measuredWidth + verticalScrollBar.minWidth;
            }
        }

        commitProperties();
    }

    // from List
    override protected function makeListData (data :Object, uid :String, rowNum :int) :BaseListData
    {
        // Oh, FFS.
        // We need to set up these "maxMeasuredIconWidth" fields on the MenuListData, but our
        // superclass has made those variables private.
        // We can get the values out of another MenuListData, so we just always call super()
        // to create one of those, and if we need to make a CommandListData, we construct one
        // from the fields in the MenuListData.

        var menuListData :MenuListData = super.makeListData(data, uid, rowNum) as MenuListData;

        var iconObject :IFlexDisplayObject = getItemProp(data, "iconObject") as IFlexDisplayObject;
        if (iconObject != null) {
            var cmdListData :CommandListData = new CommandListData(menuListData.label,
                menuListData.icon, iconObject, labelField, uid, this, rowNum);

            cmdListData.maxMeasuredIconWidth = menuListData.maxMeasuredIconWidth;
            cmdListData.maxMeasuredTypeIconWidth = menuListData.maxMeasuredTypeIconWidth;
            cmdListData.maxMeasuredBranchIconWidth = menuListData.maxMeasuredBranchIconWidth;
            cmdListData.useTwoColumns = menuListData.useTwoColumns;
            return cmdListData;
        }

        return menuListData;
    }

    /**
     * Get the specified property for the specified item, if any.  Somewhat similar to bits in the
     * DefaultDataDescriptor.
     */
    protected function getItemProp (item :Object, prop :String) :Object
    {
        try {
            if (item is XML) {
                return String((item as XML).attribute(prop));

            } else if (prop in item) {
                return item[prop];
            }

        } catch (e :Error) {
            // alas; fall through
        }
        return null;
    }

    protected var _dispatcher :IEventDispatcher;
    protected var _lefting :Boolean = false;
    protected var _upping :Boolean = false;
    protected var _fitting :Rectangle = null;
    protected var _verticalScrollPolicy :String;
}
}

Allow CommandMenu subclasses to provide their own item renderer.

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


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

package com.threerings.flex {

import flash.display.DisplayObject;

import mx.core.UIComponent;

/**
 * Wraps a non-Flex component for use in Flex.
 */
public class FlexWrapper extends UIComponent
{
    public function FlexWrapper (object :DisplayObject)
    {
        // don't capture mouse events in this wrapper
        mouseEnabled = false;
        addChild(object);
    }
}
}

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

package com.threerings.flex {

import flash.display.DisplayObject;

import mx.core.UIComponent;

/**
 * Wraps a non-Flex component for use in Flex.
 */
public class FlexWrapper extends UIComponent
{
    public function FlexWrapper (object :DisplayObject)
    {
        // don't capture mouse events in this wrapper
        mouseEnabled = false;
        _obj = object;
        addChild(object);
    }

    override protected function measure () :void
    {
        measuredWidth = _obj.width;
        measuredHeight = _obj.height;
    }

    protected var _obj :DisplayObject;
}
}

Make our measured size the size of the thing we're wrapping.
Half the places we use FlexWrapper we do something like this anyway.
Note that you can still override this by setting the width and height
(which sets the explicitWidth, explicitHeight).

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