added new alignment spaceInsideHorizontal

error: pathspec 'src/ktu/utils/align/aligners/SpaceInsideHorizontalAligner.as' did not match any file(s) known to git

--- src/ktu/utils/align/aligners/SpaceInsideHorizontalAligner.as
+++ src/ktu/utils/align/aligners/SpaceInsideHorizontalAligner.as
@@ -0,0 +1,42 @@
+
+
+package ktu.utils.align.aligners {
+
+	import flash.geom.Rectangle;
+	import flash.utils.Dictionary;
+	import ktu.utils.align.IRectangleAligner;
+	import ktu.utils.sorting.sort_rectangle_left;
+
+	/**
+	 * SpaceHorizontalAligner evenly spaces the targets along the x axis inside of the target coordinate space
+	 * @author ktu
+	 */
+	public class SpaceInsideHorizontalAligner implements IRectangleAligner {
+
+		/**
+		 * evenly spaces the targets along the x axis inside of the target coordinate space
+		 *
+		 * @param	targetCoordinateSpace
+		 * @param	targets
+		 */
+		public function alignRectangles(targetCoordinateSpace:Rectangle, targets:Array/*Rectangle*/):void {
+			targets = targets.concat();																// 	copy targets into new array so i do not ruin ordering
+			targets = targets.sort ( sort_rectangle_left ) ;										//	sort targets on left edge
+
+			var objsWidth:Number = 0;																//	total width of all targets
+			var totalWidth:Number = targetCoordinateSpace.width;									//	total width of tcs
+			var length:int = targets.length;														// 	targets length for optimized looping
+			for ( var i:int = 0; i < length; i++ )	 												//	loop (each target)
+				objsWidth += targets[i].width ;														//		add width of target to total targets width
+
+			var spread:Number = ( totalWidth - objsWidth ) / ( length - 1 ) ;						//	the gap between each target
+			var right:Number = 0//targetCoordinateSpace.left + targets[0].width;						//	current right edge of the last placed target
+			//targets[0].x = targetCoordinateSpace.left;												//	place first target
+			for ( var j:int = 0; j < length; j++ ) {												//	loop (each target - first)
+				var item:Rectangle = targets[j];													//		grab current item
+				item.x = right + spread;															//		place item at right edge plus spread
+				right += item.width + spread;														//		update right edge to include current item
+			}																						//	end loop
+		}
+	}
+}


Store ApplicationDomain.currentDomain in Injector#_applicationDomain in case of no explicit domain being set. Turns out that calling ApplicationDomain.currentDomain has a huge cost that can thus be saved in 99.9% of all cases

--- src/org/swiftsuspenders/Injector.as
+++ src/org/swiftsuspenders/Injector.as
@@ -38,6 +38,7 @@
 			_mappings = new Dictionary();
 			_namedInjectionsManager = new NamedInjectionsManager(this);
 			_classDescriptor = new ClassDescriptor(INJECTION_POINTS_CACHE);
+			_applicationDomain = ApplicationDomain.currentDomain;
 		}

 		public function map(dependency : Class) : InjectionRule
@@ -152,11 +153,11 @@

 		public function set applicationDomain(applicationDomain : ApplicationDomain) : void
 		{
-			_applicationDomain = applicationDomain;
+			_applicationDomain = applicationDomain || ApplicationDomain.currentDomain;
 		}
 		public function get applicationDomain() : ApplicationDomain
 		{
-			return _applicationDomain ? _applicationDomain : ApplicationDomain.currentDomain;
+			return _applicationDomain;
 		}

 		public function usingName(name : String) : NamedInjectionsManager


FIX: would throw an error if capturing without params

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/rafaelcordoba/cocktail-demo.git/'


#3 Add support for SIDX box version 1

#3 Add support for SIDX box version 1

error: pathspec 'src/main/actionscript/com/castlabs/dash/utils/Bytes.as' did not match any file(s) known to git

--- src/main/actionscript/com/castlabs/dash/utils/Bytes.as
+++ src/main/actionscript/com/castlabs/dash/utils/Bytes.as
@@ -0,0 +1,6 @@
+package com.castlabs.dash.utils {
+public class Bytes {
+    public function Bytes() {
+    }
+}
+}


[CEXP-257] MicrophoneLevelEvent: Unit tests.

error: pathspec 'src/test/actionscript/MicrophoneLevelEventTest.as' did not match any file(s) known to git

--- src/test/actionscript/MicrophoneLevelEventTest.as
+++ src/test/actionscript/MicrophoneLevelEventTest.as
@@ -0,0 +1,43 @@
+package
+{
+
+import org.hamcrest.assertThat;
+import org.hamcrest.core.isA;
+import org.hamcrest.object.equalTo;
+import org.hamcrest.object.isFalse;
+import org.hamcrest.object.isTrue;
+import org.hamcrest.object.notNullValue;
+import org.hamcrest.object.nullValue;
+
+import MicrophoneLevelEvent;
+
+public class MicrophoneLevelEventTest
+{
+  private var microphoneLevelEvent:MicrophoneLevelEvent;
+
+  [Test]
+  public function should_have_static_constant_property_in_type_of_string():void {
+    // then
+    assertThat(MicrophoneLevelEvent.LEVEL_VALUE, isA(String));
+  }
+
+  [Test]
+  public function should_have_type_property_equal_to_its_static_constant_property():void {
+    // when
+    microphoneLevelEvent = new MicrophoneLevelEvent(23);
+    // then
+    assertThat(microphoneLevelEvent.type, equalTo(MicrophoneLevelEvent.LEVEL_VALUE));
+  }
+
+  [Test]
+  public function should_allow_getting_value_provided_while_initialization():void {
+    // given
+    var levelValue:Number = 23;
+    // when
+    microphoneLevelEvent = new MicrophoneLevelEvent(levelValue);
+    // then
+    assertThat( microphoneLevelEvent.getLevelValue(), equalTo(levelValue));
+  }
+
+}
+}


globalLocation() function renamed registrationPoint(). Added topLeftPoint() function.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/rlowke/lowkemedia.git/'


Add console.log.

Add console.log.

error: pathspec 'src/console.as' did not match any file(s) known to git

--- src/console.as
+++ src/console.as
@@ -0,0 +1,7 @@
+package {
+  public class console {
+    public static function log(string: String): void {
+      process.puts(string)
+    }
+  }
+}


Fixed potential race condition

Fixed potential race condition

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/7thsky/openvv.git/'


Added a comment

Added a comment


fix reading user data

fix reading user data

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/dseeker/lietro.git/'


starting inline datasource

starting inline datasource


Add class ImpetusChannel

Add class ImpetusChannel

error: pathspec 'ImpetusChannel.as' did not match any file(s) known to git

--- ImpetusChannel.as
+++ ImpetusChannel.as
@@ -0,0 +1,14 @@
+package io.github.jwhile.impetus
+{
+    import flash.media.SoundChannel;
+
+    public class ImpetusChannel
+    {
+        this.channel:SoundChannel;
+
+        public function ImpetusChannel(channel:SoundChannel):void
+        {
+            this.channel = channel;
+        }
+    }
+}


removing trace

removing trace


Made optional String parameters default to empty quotes instead of null (more proper).

--- src/org/swiftsuspenders/Injector.as
+++ src/org/swiftsuspenders/Injector.as
@@ -56,7 +56,7 @@
 		}

 		public function mapValue(
-			whenAskedFor : Class, useValue : Object, named : String = null) : void
+			whenAskedFor : Class, useValue : Object, named : String = "") : void
 		{
 			var config : InjectionConfig = new InjectionConfig(
 				whenAskedFor, useValue, InjectionConfig.INJECTION_TYPE_VALUE);
@@ -64,20 +64,20 @@
 		}

 		public function mapClass(
-			whenAskedFor : Class, instantiateClass : Class, named : String = null) : void
+			whenAskedFor : Class, instantiateClass : Class, named : String = "") : void
 		{
 			var config : InjectionConfig = new InjectionConfig(
 				whenAskedFor, instantiateClass, InjectionConfig.INJECTION_TYPE_CLASS);
 			addMapping(config, named);
 		}

-		public function mapSingleton(whenAskedFor : Class, named : String = null) : void
+		public function mapSingleton(whenAskedFor : Class, named : String = "") : void
 		{
 			mapSingletonOf(whenAskedFor, whenAskedFor, named);
 		}

 		public function mapSingletonOf(
-			whenAskedFor : Class, useSingletonOf : Class, named : String = null) : void
+			whenAskedFor : Class, useSingletonOf : Class, named : String = "") : void
 		{
 			var config : InjectionConfig = new InjectionConfig(
 				whenAskedFor, useSingletonOf, InjectionConfig.INJECTION_TYPE_SINGLETON);
@@ -96,7 +96,7 @@
 			var ctor : Class;
 			if (target is Proxy)
 			{
-				//for classes extending Proxy, we can't access the 'constructor' property because the
+				//for classes extending Proxy, we can't access the 'constructor' property because the
 				//Proxy will throw if we try. So let's take the scenic route ...
 				var name : String = getQualifiedClassName(target);
 				ctor = Class(getDefinitionByName(name));
@@ -128,7 +128,7 @@
 			return instance;
 		}

-		public function unmap(clazz : Class, named : String = null) : void
+		public function unmap(clazz : Class, named : String = "") : void
 		{
 			var requestName : String = getQualifiedClassName(clazz);
 			if (named && m_mappings[named])
@@ -197,4 +197,4 @@
 			return injectionPoints;
 		}
 	}
-}+}


Removed deletehint while selecting

Removed deletehint while selecting

--- src/LevelStates/LevelState.as
+++ src/LevelStates/LevelState.as
@@ -702,7 +702,7 @@
 			undoButton.setAlpha(undoAlpha);
 			redoButton.setAlpha(undoAlpha);

-			deleteHint.exists = !currentWire && !currentBloc && !currentModule && (findMousedModule() || findMousedWire());
+			deleteHint.exists = !currentWire && !currentBloc && !currentModule && !selectionArea && (findMousedModule() || findMousedWire());

 			if (loadButton) {
 				var successSave:String = findSuccessSave();


Removed useless whitespace

Removed useless whitespace

--- src/org/mangui/hls/HLS.as
+++ src/org/mangui/hls/HLS.as
@@ -259,7 +259,7 @@
         public function set audioTrack(val : int) : void {
             _audioTrackController.audioTrack = val;
         }
-
+
         /* set stage */
         public function set stage(stage : Stage) : void {
             _stage = stage;


The new super for all components: resistors, wires, capacitors...

error: pathspec 'activities/module-2/series-measuring/ComponentObj.as' did not match any file(s) known to git

--- activities/module-2/series-measuring/ComponentObj.as
+++ activities/module-2/series-measuring/ComponentObj.as
@@ -0,0 +1,25 @@
+package
+{
+	import flash.display.MovieClip;
+	import flash.display.Shape;
+	import flash.display.Graphics;
+	import flash.display.DisplayObject;
+	import flash.display.CapsStyle;
+	import flash.display.Sprite;
+
+
+	public class ComponentObj extends MovieClip{
+
+		private var _breadboard:MovieClip;
+
+		public function ComponentObj() {
+			trace('ENTER ComponentObj');
+
+			super();
+			//_breadboard = breadboard;
+			//_breadboard.getComponentLayer().addChild(this);
+		}
+
+
+	}
+}


implemented NormalizedColumn

implemented NormalizedColumn

error: pathspec 'WeaveData/src/weave/data/AttributeColumns/NormalizedColumn.as' did not match any file(s) known to git

--- WeaveData/src/weave/data/AttributeColumns/NormalizedColumn.as
+++ WeaveData/src/weave/data/AttributeColumns/NormalizedColumn.as
@@ -0,0 +1,70 @@
+/*
+    Weave (Web-based Analysis and Visualization Environment)
+    Copyright (C) 2008-2011 University of Massachusetts Lowell
+
+    This file is a part of Weave.
+
+    Weave is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License, Version 3,
+    as published by the Free Software Foundation.
+
+    Weave 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Weave.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package weave.data.AttributeColumns
+{
+	import weave.api.WeaveAPI;
+	import weave.api.data.IColumnStatistics;
+	import weave.api.data.IQualifiedKey;
+	import weave.api.getCallbackCollection;
+	import weave.api.newLinkableChild;
+	import weave.core.LinkableNumber;
+
+	/**
+	 * @author adufilie
+	 */
+	public class NormalizedColumn extends ExtendedDynamicColumn
+	{
+		public function NormalizedColumn(min:Number = 0, max:Number = 1)
+		{
+			_stats = WeaveAPI.StatisticsCache.getColumnStatistics(internalDynamicColumn);
+			// when stats update, we need to trigger our callbacks because the values returned by getValueFromKey() will be different.
+			getCallbackCollection(_stats).addImmediateCallback(this, triggerCallbacks);
+
+			this.min.value = min;
+			this.max.value = max;
+		}
+
+		private var _stats:IColumnStatistics;
+
+		public const min:LinkableNumber = newLinkableChild(this, LinkableNumber);
+		public const max:LinkableNumber = newLinkableChild(this, LinkableNumber);
+
+		/**
+		 * getValueFromKey
+		 * @param key A key of the type specified by keyType.
+		 * @return The value associated with the given key.
+		 */
+		override public function getValueFromKey(key:IQualifiedKey, dataType:Class = null):*
+		{
+			if (dataType == null)
+				dataType = Number;
+
+			if (dataType == Number)
+			{
+				// get norm value between 0 and 1
+				var norm:Number = _stats.getNorm(key);
+				// return number between min and max
+				return min.value + norm * (max.value - min.value);
+			}
+
+			return super.getValueFromKey(key, dataType);
+		}
+	}
+}


add simple socket server

add simple socket server

git-svn-id: 44dac414ef660a76a1e627c7b7c0e36d248d62e4@13 c2cce57b-6cbb-4450-ba62-5fdab5196ef5

error: pathspec 'src/corsaair/server/spitfire/SimpleSocketServer.as' did not match any file(s) known to git

--- src/corsaair/server/spitfire/SimpleSocketServer.as
+++ src/corsaair/server/spitfire/SimpleSocketServer.as
@@ -0,0 +1,249 @@
+/* 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 corsaair.server.spitfire
+{
+
+    import C.errno.*;
+    import C.arpa.inet.*;
+    import C.netdb.*;
+    import C.netinet.*;
+    import C.sys.socket.*;
+    import C.stdlib.*;
+    import C.unistd.*;
+
+    import flash.utils.ByteArray;
+
+
+    /**
+     * A simple socket server.
+     *
+     * This mainyl show how you use BSD sockets to setup a listenign server,
+     * the server does not have a loop to listen on multiple connections
+     * to deal with numerous clients.
+     *
+     * You run the server, connect to it with telnet or netcat,
+     * the server send a message to the client and then close the connection.
+     *
+     * @see http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#simpleserver A Simple Stream Server
+     */
+    public class SimpleSocketServer
+    {
+
+        // the port users will be connecting to
+        public const PORT:String = "3490";
+
+        // how many pending connections queue will hold
+        public const BACKLOG:uint = 10;
+
+        public function SimpleSocketServer()
+        {
+            super();
+        }
+
+        /* Note:
+           This is our first server
+           we do the basic and the very raw stuff
+           using BSD sockets with comments :)
+
+           this code is ported directly from C source code
+           http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#simpleserver
+           from the excellent
+           Beej's Guide to Network Programming
+           Using Internet Sockets
+        */
+        public function main():void
+        {
+            // listen on sock_fd
+            var sockfd:int;
+            // new connection on new_fd
+            var new_fd:int;
+
+            var hints:addrinfo = new addrinfo();
+            var servinfo:addrinfo;
+
+            /* Note:
+               With BSD sockets the constants are very important
+               it's how we configure everything
+
+               ai_family = AF_UNSPEC
+               if we want to use either IPv4 or IPv6
+
+               ai_family = AF_INET
+               to use only IPv4
+
+               ai_family = AF_INET6
+               to use only IPv6
+
+               SOCK_STREAM for TCP (stream) sockets
+               SOCK_DGRAM for UDP (datagram) sockets
+            */
+            hints.ai_family   = AF_UNSPEC;
+            hints.ai_socktype = SOCK_STREAM;
+            hints.ai_flags    = AI_PASSIVE; // use my IP
+
+            /* Note:
+               getaddrinfo() works a bit differently compared to C
+               instead of passing a list by reference and return an int
+               our AS3 getaddrinfo()
+               - return null if an error occured
+                 and will fill the details into the CEAIrror object
+               - or return an array of addrinfo found
+            */
+            var eaierr:CEAIrror = new CEAIrror();
+            var addrlist:Array  = getaddrinfo( null, PORT, hints, eaierr );
+
+            if( !addrlist )
+            {
+                throw eaierr;
+            }
+
+            var option:int;
+            var bound:int;
+
+            // loop through all the results and bind to the first we can
+            for( var i:uint = 0; i < addrlist.length; i++ )
+            {
+                servinfo = addrlist[i];
+
+                sockfd = socket( servinfo.ai_family, servinfo.ai_socktype, servinfo.ai_protocol );
+                if( sockfd == -1 )
+                {
+                    trace( "server: socket" )
+                    trace( new CError( "", errno ) );
+                    continue;
+                }
+
+                option = setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, 1 );
+                if( option == -1 )
+                {
+                    trace( "setsockopt" );
+                    trace( new CError( "", errno ) );
+                    exit( 1 );
+                }
+
+                bound = bind( sockfd, servinfo.ai_addr );
+                if( bound == -1 )
+                {
+                    close( sockfd );
+                    trace( "server: bind" );
+                    trace( new CError( "", errno ) );
+                    continue;
+                }
+
+                /* Note:
+                   If we reach here that means we could
+                   open a socket and bind to it
+                */
+                break;
+            }
+
+            /* Note:
+               If this happen that means
+               we looped trough all the address of getaddrinfo()
+               but we could not open/bind any of them
+            */
+            if( servinfo == null )
+            {
+                trace( "server: failed to bind" );
+                exit( 1 );
+            }
+
+            /* Note:
+               that's the basic of a "socket server"
+               - socket() : Create an endpoint for communication.
+               - bind()   : Bind a name to a socket.
+               - listen() : Listen for socket connections
+                            and limit the queue of incoming connections.
+
+               socket() is mainly describing what kind of socket we want to use
+               and then return a file descriptor (the ID of the socket if you want)
+
+               bind() is here to "connect" the socket ID to a network interface
+               on the same computer you could have different IP address:
+               127.0.0.1 the localhost
+               192.168.0.1 the IP adress on your local network
+               255.165.225.235 the IP adress assigned by your ISP
+               etc.
+
+               and finally listen() is here to say on which port we want
+               to receive data, or which service
+               port 80 is for HTTP web server
+               port 21 is for FTP server
+               etc.
+            */
+            var listening:int = listen( sockfd, BACKLOG );
+            if( listening == -1 )
+            {
+                trace( "listen" );
+                trace( new CError( "", errno ) );
+                exit( 1 );
+            }
+
+
+            trace( "server: waiting for connections..." );
+
+            var client_addr:sockaddr_in = new sockaddr_in();
+
+            // main accept() loop
+            while( 1 )
+            {
+                new_fd = accept( sockfd, client_addr );
+                if( new_fd == -1 )
+                {
+                    trace( "accept" );
+                    trace( new CError( "", errno ) );
+                    continue;
+                }
+
+                var s:String = inet_ntop( client_addr.sin_family, client_addr );
+                if( !s )
+                {
+                    trace( "inet_ntop" );
+                    trace( new CError( "", errno ) );
+                    continue;
+                }
+                trace( "server: got connection from " + s );
+
+                /* Note:
+                   we prepare the message into a ByteArray and send it
+
+                   If you comapre with server.c you will see that
+                   we do not use fork()
+                   this is on purpose
+                   fork() is a very unixy thing that just does not work
+                   under Windows, but next example will show how to do
+                   like fork() but with Workers :)
+                */
+                var msg:String = "Hello, world!\n";
+                var bytes:ByteArray = new ByteArray();
+                    bytes.writeUTFBytes( msg );
+                    bytes.position = 0;
+                var sent:int = send( new_fd, bytes );
+                if( sent == -1 )
+                {
+                    trace( "send" );
+                    trace( new CError( "", errno ) );
+                    continue;
+                }
+                else
+                {
+                    trace( "sent " + sent + " bytes to the client" );
+                    break;
+                }
+
+            }
+
+            trace( "disconnecting client" );
+            close( new_fd );
+
+            trace( "shutting down server" );
+            close( sockfd );
+
+            exit( 0 );
+
+        }
+
+    }
+}


making logger be instanciated only when used


Issue 64; Fix problem with getSVGParent which could skip a parent.  Also fixed problem where filter can be applied to mask.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/leymannx/svgweb.git/'


renaming methos and some more code clean up


Make button slider not focusable so it is easier to tab around.

--- bin/Data/Scripts/Editor/AttributeEditor.as
+++ bin/Data/Scripts/Editor/AttributeEditor.as
@@ -941,6 +941,7 @@
     dragSld.SetFixedHeight(ATTR_HEIGHT - 3);
     dragSld.SetFixedWidth(dragSld.height);
     dragSld.SetAlignment(HA_RIGHT, VA_TOP);
+    dragSld.focusMode = FM_NOTFOCUSABLE;
     parent.AddChild(dragSld);

     SubscribeToEvent(dragSld, "DragBegin", "LineDragBegin");


FLEX-26808 Adding unit test. For now we're just testing that Ctrl+Click deselects the (selected) first item (test passes). Dragging behaviour will follow.

error: pathspec 'frameworks/projects/spark/tests/spark/components/DataGrid_FLEX_26808_Tests.as' did not match any file(s) known to git

--- frameworks/projects/spark/tests/spark/components/DataGrid_FLEX_26808_Tests.as
+++ frameworks/projects/spark/tests/spark/components/DataGrid_FLEX_26808_Tests.as
@@ -0,0 +1,203 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.components {
+    import flash.events.Event;
+    import flash.events.EventDispatcher;
+    import flash.events.MouseEvent;
+
+    import mx.collections.ArrayCollection;
+    import mx.collections.ArrayList;
+    import mx.collections.IList;
+    import mx.collections.ListCollectionView;
+
+    import org.flexunit.asserts.assertEquals;
+    import org.flexunit.asserts.assertNull;
+    import org.flexunit.asserts.assertTrue;
+    import org.flexunit.async.Async;
+    import org.fluint.uiImpersonation.UIImpersonator;
+
+    import spark.components.gridClasses.GridColumn;
+    import spark.components.gridClasses.GridSelectionMode;
+    import spark.events.GridEvent;
+
+    public class DataGrid_FLEX_26808_Tests
+    {
+        private static const NO_ENTER_FRAMES_TO_ALLOW:int = 2;
+        private static const _finishNotifier:EventDispatcher = new EventDispatcher();
+        private static var noEnterFramesRemaining:int = NaN;
+        private var _sut:DataGridInspectable;
+        private var _firstObject:FLEX_26808_VO;
+
+        [Before]
+        public function setUp():void
+        {
+            _sut = new DataGridInspectable();
+
+            _sut.dragEnabled = true;
+            _sut.selectionMode = GridSelectionMode.MULTIPLE_ROWS;
+            _sut.columns = new ArrayCollection([new GridColumn("name")]);
+            _sut.width = 200;
+            _sut.height = 200;
+
+            const tenObjects:IList = generateVOs(10);
+            _firstObject = tenObjects.getItemAt(0) as FLEX_26808_VO;
+            const dataProvider:ListCollectionView = new ListCollectionView(tenObjects);
+            _sut.dataProvider = dataProvider;
+        }
+
+        [After]
+        public function tearDown():void
+        {
+            _sut = null;
+            noEnterFramesRemaining = NaN;
+        }
+
+        [Test(async, timeout=1000)]
+        public function test_ctrl_click_removes_selected_item():void
+        {
+            //when
+            UIImpersonator.addElement(_sut);
+
+            //then
+            assertNull(_sut.selectedItem);
+
+            noEnterFramesRemaining = NO_ENTER_FRAMES_TO_ALLOW;
+            UIImpersonator.testDisplay.addEventListener(Event.ENTER_FRAME, onEnterFrame);
+            Async.handleEvent(this, _finishNotifier, Event.COMPLETE, testSelectionAndDeselection, 800);
+        }
+
+        private function testSelectionAndDeselection(event:Event, passThroughData:Object):void
+        {
+            function onGridMouseDown(event:GridEvent):void
+            {
+                assertEquals(0, event.rowIndex);
+                assertTrue(event.ctrlKey);
+            }
+
+            //when
+            _sut.setSelectedIndex(0);
+
+            //then
+            assertEquals(_firstObject, _sut.selectedItem);
+
+            //given
+            const mouseDown:MouseEvent = new MouseEvent(MouseEvent.MOUSE_DOWN, true, false, 5, 5, null, true, false, false);
+            const mouseUp:MouseEvent = new MouseEvent(MouseEvent.MOUSE_UP, true, false, 5, 5, null, true, false, false);
+            _sut.addEventListener(GridEvent.GRID_MOUSE_DOWN, onGridMouseDown);
+
+            //when - Ctrl+Click on first item to deselect it
+            _sut.grid.dispatchEvent(mouseDown);
+            _sut.grid.dispatchEvent(mouseUp);
+
+            //then
+            assertEquals(1, _sut.commitInteractiveSelection_calls);
+            assertNull("The selection should have been removed due to Ctrl+Click!", _sut.selectedItem);
+            _sut.removeEventListener(GridEvent.GRID_MOUSE_DOWN, onGridMouseDown);
+        }
+
+        private static function onEnterFrame(event:Event):void
+        {
+            if(!--noEnterFramesRemaining)
+            {
+                UIImpersonator.testDisplay.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
+                _finishNotifier.dispatchEvent(new Event(Event.COMPLETE));
+            }
+        }
+
+        private static function generateVOs(no:int, reverse:Boolean = false):IList
+        {
+            return generateObjects(no, reverse, generateOneObject);
+        }
+
+        private static function generateObjects(no:int, reverse:Boolean, generator:Function):IList
+        {
+            var result:Array = [];
+            for(var i:int = 0; i < no; i++)
+            {
+                result.push(generator(i));
+            }
+
+            if(reverse)
+                result.reverse();
+
+            return new ArrayList(result);
+        }
+
+        private static function generateOneObject(i:Number):FLEX_26808_VO
+        {
+            return new FLEX_26808_VO(i, "Object", "Street");
+        }
+    }
+}
+
+import spark.components.DataGrid;
+import spark.skins.spark.DataGridSkin;
+
+[Bindable]
+class FLEX_26808_VO
+{
+    public var name:String;
+    public var address:FLEX_26808_AddressVO;
+    public var index:Number;
+
+    public function FLEX_26808_VO(index:Number, namePrefix:String, streetPrefix:String)
+    {
+        this.index = index;
+        this.name = namePrefix + index;
+        this.address = new FLEX_26808_AddressVO(streetPrefix + index, Math.floor(index), new Date(2000 + Math.floor(index), Math.floor(index), 1, 0, 0, 0, 1));
+    }
+}
+
+[Bindable]
+class FLEX_26808_AddressVO
+{
+    public var street:String;
+    public var houseNumber:int;
+    public var dateMovedIn:Date;
+
+    public function FLEX_26808_AddressVO(street:String, houseNumber:int, dateMovedIn:Date)
+    {
+        this.street = street;
+        this.houseNumber = houseNumber;
+        this.dateMovedIn = dateMovedIn;
+    }
+}
+
+class DataGridInspectable extends DataGrid
+{
+    public function DataGridInspectable()
+    {
+        super();
+        this.setStyle("skinClass", DataGridSkin);
+    }
+
+    public var commitInteractiveSelection_calls:int = 0;
+
+    override protected function commitInteractiveSelection(selectionEventKind:String,
+                                                           rowIndex:int,
+                                                           columnIndex:int,
+                                                           rowCount:int = 1,
+                                                           columnCount:int = 1):Boolean
+
+    {
+        commitInteractiveSelection_calls++;
+        return super.commitInteractiveSelection(selectionEventKind, rowIndex, columnIndex, rowCount, columnCount);
+    }
+}


add original FragmentLoader

add original FragmentLoader

error: pathspec 'src/org/mangui/hls/loader/FragmentLoader.as' did not match any file(s) known to git

--- src/org/mangui/hls/loader/FragmentLoader.as
+++ src/org/mangui/hls/loader/FragmentLoader.as
@@ -0,0 +1,1189 @@
+/* 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 flash.events.*;
+    import flash.net.*;
+    import flash.utils.ByteArray;
+    import flash.utils.Timer;
+    import flash.utils.getTimer;
+
+    import org.mangui.hls.HLS;
+    import org.mangui.hls.HLSSettings;
+    import org.mangui.hls.constant.HLSLoaderTypes;
+    import org.mangui.hls.constant.HLSTypes;
+    import org.mangui.hls.controller.AudioTrackController;
+    import org.mangui.hls.controller.LevelController;
+    import org.mangui.hls.demux.DemuxHelper;
+    import org.mangui.hls.demux.Demuxer;
+    import org.mangui.hls.demux.ID3Tag;
+    import org.mangui.hls.event.HLSError;
+    import org.mangui.hls.event.HLSEvent;
+    import org.mangui.hls.event.HLSLoadMetrics;
+    import org.mangui.hls.flv.FLVTag;
+    import org.mangui.hls.model.AudioTrack;
+    import org.mangui.hls.model.Fragment;
+    import org.mangui.hls.model.FragmentData;
+    import org.mangui.hls.model.Level;
+    import org.mangui.hls.stream.StreamBuffer;
+    import org.mangui.hls.utils.AES;
+
+    import org.hola.JSURLStream;
+
+    CONFIG::LOGGING {
+        import org.mangui.hls.utils.Log;
+        import org.mangui.hls.utils.Hex;
+    }
+    /** Class that fetches fragments. **/
+    public class FragmentLoader {
+        /** Reference to the HLS controller. **/
+        private var _hls : HLS;
+        /** reference to auto level manager */
+        private var _levelController : LevelController;
+        /** reference to audio track controller */
+        private var _audioTrackController : AudioTrackController;
+        /** has manifest just being reloaded **/
+        private var _manifestJustLoaded : Boolean;
+        /** last loaded level. **/
+        private var _levelLastLoaded : int;
+        /** next level (-1 if not defined yet) **/
+        private var _levelNext : int = -1;
+        /** Reference to the manifest levels. **/
+        private var _levels : Vector.<Level>;
+        /** Util for loading the fragment. **/
+        private var _fragstreamloader : URLStream;
+        /** Util for loading the key. **/
+        private var _keystreamloader : URLStream;
+        /** key map **/
+        private var _keymap : Object;
+        /** Did the stream switch quality levels. **/
+        private var _switchLevel : Boolean;
+        /** Did a discontinuity occurs in the stream **/
+        private var _hasDiscontinuity : Boolean;
+        /** boolean to track whether PTS analysis is ongoing or not */
+        private var _ptsAnalyzing : Boolean = false;
+        /** Timer used to monitor/schedule fragment download. **/
+        private var _timer : Timer;
+        /** requested seek position **/
+        private var _seekPosition : Number;
+        /** first fragment loaded ? **/
+        private var _fragmentFirstLoaded : Boolean;
+        /* demux instance */
+        private var _demux : Demuxer;
+        /* stream buffer instance **/
+        private var _streamBuffer : StreamBuffer;
+        /* key error/reload */
+        private var _keyLoadErrorDate : Number;
+        private var _keyRetryTimeout : Number;
+        private var _keyRetryCount : int;
+        private var _keyLoadStatus : int;
+        /* fragment error/reload */
+        private var _fragLoadErrorDate : Number;
+        private var _fragRetryTimeout : Number;
+        private var _fragRetryCount : int;
+        private var _fragLoadStatus : int;
+        private var _fragSkipping : Boolean;
+        private var _fragSkipCount : int;
+        /** reference to previous/current fragment */
+        private var _fragPrevious : Fragment;
+        private var _fragCurrent : Fragment;
+        /* loading state variable */
+        private var _loadingState : int;
+        /* loading metrics */
+        private var _metrics : HLSLoadMetrics;
+        private static const LOADING_STOPPED : int = -1;
+        private static const LOADING_IDLE : int = 0;
+        private static const LOADING_IN_PROGRESS : int = 1;
+        private static const LOADING_WAITING_LEVEL_UPDATE : int = 2;
+        private static const LOADING_STALLED : int = 3;
+        private static const LOADING_FRAGMENT_IO_ERROR : int = 4;
+        private static const LOADING_KEY_IO_ERROR : int = 5;
+        private static const LOADING_COMPLETED : int = 6;
+
+        /** Create the loader. **/
+        public function FragmentLoader(hls : HLS, audioTrackController : AudioTrackController, levelController : LevelController, streamBuffer : StreamBuffer) : void {
+            _hls = hls;
+            _levelController = levelController;
+            _audioTrackController = audioTrackController;
+            _streamBuffer = streamBuffer;
+            _hls.addEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
+            _hls.addEventListener(HLSEvent.LEVEL_LOADED, _levelLoadedHandler);
+            _hls.addEventListener(HLSEvent.LEVEL_LOADING_ABORTED, _levelLoadingAbortedHandler);
+            _timer = new Timer(20, 0);
+            _timer.addEventListener(TimerEvent.TIMER, _checkLoading);
+            _loadingState = LOADING_STOPPED;
+            _manifestJustLoaded = false;
+            _keymap = new Object();
+        };
+
+        public function dispose() : void {
+            stop();
+            _timer.removeEventListener(TimerEvent.TIMER, _checkLoading);
+            _hls.removeEventListener(HLSEvent.MANIFEST_LOADED, _manifestLoadedHandler);
+            _hls.removeEventListener(HLSEvent.LEVEL_LOADED, _levelLoadedHandler);
+            _hls.removeEventListener(HLSEvent.LEVEL_LOADING_ABORTED, _levelLoadingAbortedHandler);
+            _loadingState = LOADING_STOPPED;
+            _keymap = new Object();
+        }
+
+        public function get audioExpected() : Boolean {
+            if (_demux) {
+                return _demux.audioExpected;
+            } else {
+                // always return true in case demux is not yet initialized
+                return true;
+            }
+        }
+
+        public function get videoExpected() : Boolean {
+            if (_demux) {
+                return _demux.videoExpected;
+            } else {
+                // always return true in case demux is not yet initialized
+                return true;
+            }
+        }
+
+        /**  fragment loading Timer **/
+        private function _checkLoading(e : Event) : void {
+            switch(_loadingState) {
+                // nothing to load, stop fragment loader.
+                case LOADING_STOPPED:
+                    stop();
+                    break;
+                // nothing to load until level is retrieved
+                case LOADING_WAITING_LEVEL_UPDATE:
+                    break;
+                // loading already in progress
+                case LOADING_IN_PROGRESS:
+                    /* only monitor fragment loading rate if in auto mode, AND
+                       we are not loading the first segment AND
+                       current level is not the lowest level */
+                    if(_hls.autoLevel && !_manifestJustLoaded && _fragCurrent.level) {
+                        // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate
+                        var requestDelay : int = getTimer() - _metrics.loading_request_time;
+                        var fragDuration : Number = _fragCurrent.duration;
+                        if(requestDelay > 500*fragDuration) {
+                            var loaded : int = _fragCurrent.data.bytesLoaded;
+                            var expected : int = fragDuration*_levels[_fragCurrent.level].bitrate/8;
+                            if(expected < loaded) {
+                                expected = loaded;
+                            }
+                            var loadRate : int = loaded*1000/requestDelay; // byte/s
+                            var fragLoadedDelay : Number =(expected-loaded)/loadRate;
+                            var fragLevel0LoadedDelay : Number = fragDuration*_levels[0].bitrate/(8*loadRate); //bps/Bps
+                            var bufferLen : Number = _hls.stream.bufferLength;
+                            // CONFIG::LOGGING {
+                            //     Log.info("bufferLen/fragDuration/fragLoadedDelay/fragLevel0LoadedDelay:" + bufferLen.toFixed(1) + "/" + fragDuration.toFixed(1) + "/" + fragLoadedDelay.toFixed(1) + "/" + fragLevel0LoadedDelay.toFixed(1));
+                            // }
+                            /* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer len
+                              ... and also bigger than duration needed to load fragment at next level ...*/
+                            if(bufferLen < 2*fragDuration && fragLoadedDelay > bufferLen && fragLoadedDelay > fragLevel0LoadedDelay) {
+                                // try to abort fragment loading ...
+                                // try to flush last fragment seamlessly
+                                if(_streamBuffer.flushLastFragment(_fragCurrent.level,_fragCurrent.seqnum)) {
+                                    CONFIG::LOGGING {
+                                        Log.warn("_checkLoading : loading too slow, abort fragment loading");
+                                        Log.warn("fragLoadedDelay/bufferLen/fragLevel0LoadedDelay:" + fragLoadedDelay.toFixed(1) + "/" + bufferLen.toFixed(1) + "/" + fragLevel0LoadedDelay.toFixed(1));
+                                    }
+                                    //abort fragment loading
+                                    _stop_load();
+                                    // fill loadMetrics to please LevelController that will adjust bw for next fragment
+                                    // fill theoritical value, assuming bw will remain as it is
+                                    _metrics.size = expected;
+                                    _metrics.duration = 1000*fragDuration;
+                                    _metrics.loading_end_time = _metrics.parsing_end_time = _metrics.loading_request_time + 1000*expected/loadRate;
+                                    _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_LOAD_EMERGENCY_ABORTED, _metrics));
+                                    _levelNext = _levelController.getnextlevel(_fragCurrent.level, bufferLen);
+                                    // ensure that we really switch down to avoid looping here.
+                                    // _fragCurrent.level is gt 0 in that case, no need to Math.max(0,_levelNext)
+                                    _levelNext = Math.min(_levelNext, _fragCurrent.level-1);
+                                  // switch back to IDLE state to request new fragment at lowest level
+                                  _loadingState = LOADING_IDLE;
+                                }
+                            }
+                        }
+                    }
+                    break;
+                // no loading in progress, try to load first/next fragment
+                case LOADING_IDLE:
+                    var level : int;
+                    // check if first fragment after seek has been already loaded
+                    if (_fragmentFirstLoaded == false) {
+                        // select level for first fragment load
+                        if(_levelNext != -1) {
+                            level = _levelNext;
+                        } else if (_hls.autoLevel) {
+                            if (_manifestJustLoaded) {
+                                level = _hls.startLevel;
+                            } else {
+                                if(_hls.stream.bufferLength) {
+                                    // if buffer not empty, select level from heuristics
+                                    level = _levelController.getnextlevel(_hls.loadLevel, _hls.stream.bufferLength);
+                                } else {
+                                    // if buffer empty, retrieve seek level
+                                    level = _hls.seekLevel;
+                                }
+                            }
+                        } else {
+                            level = _hls.manualLevel;
+                        }
+                        if (level != _hls.loadLevel) {
+                            _demux = null;
+                            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_SWITCH, level));
+                        }
+                        _switchLevel = true;
+
+                        // check if we received playlist for choosen level. if live playlist, ensure that new playlist has been refreshed
+                        // to avoid loading outdated fragments
+                        if ((_levels[level].fragments.length == 0) || (_hls.type == HLSTypes.LIVE && _levelLastLoaded != level)) {
+                            // playlist not yet received
+                            CONFIG::LOGGING {
+                                Log.debug("_checkLoading : playlist not received for level:" + level);
+                            }
+                            _loadingState = LOADING_WAITING_LEVEL_UPDATE;
+                            _levelNext = level;
+                        } else {
+                            // just after seek, load first fragment
+                            _loadingState = _loadfirstfragment(_seekPosition, level);
+                        }
+
+                        /* first fragment already loaded
+                         * check if we need to load next fragment, do it only if buffer is NOT full
+                         */
+                    } else if (HLSSettings.maxBufferLength == 0 || _hls.stream.bufferLength < HLSSettings.maxBufferLength) {
+                        // select level for next fragment load
+                        if(_levelNext != -1) {
+                            level = _levelNext;
+                        } else if (_hls.autoLevel && _levels.length > 1 ) {
+                            // select level from heuristics (current level / last fragment duration / buffer length)
+                            level = _levelController.getnextlevel(_hls.loadLevel, _hls.stream.bufferLength);
+                        } else if (_hls.autoLevel && _levels.length == 1 ) {
+                            level = 0;
+                        } else {
+                            level = _hls.manualLevel;
+                        }
+                        // notify in case level switch occurs
+                        if (level != _hls.loadLevel) {
+                            _switchLevel = true;
+                            _demux = null;
+                            _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_SWITCH, level));
+                        }
+                        // check if we received playlist for choosen level. if live playlist, ensure that new playlist has been refreshed
+                        if ((_levels[level].fragments.length == 0) || (_hls.type == HLSTypes.LIVE && _levelLastLoaded != level)) {
+                            // playlist not yet received
+                            CONFIG::LOGGING {
+                                Log.debug("_checkLoading : playlist not received for level:" + level);
+                            }
+                            _loadingState = LOADING_WAITING_LEVEL_UPDATE;
+                            _levelNext = level;
+                        } else {
+                            _loadingState = _loadnextfragment(level, _fragPrevious);
+                        }
+                    }
+                    break;
+                case LOADING_STALLED:
+                    /* next consecutive fragment not found:
+                    it could happen on live playlist :
+                    - if bandwidth available is lower than lowest quality needed bandwidth
+                    - after long pause
+                    */
+                    CONFIG::LOGGING {
+                        Log.warn("loading stalled:stop fragment loading");
+                    }
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LIVE_LOADING_STALLED));
+                    stop();
+                    break;
+                // if key loading failed
+                case  LOADING_KEY_IO_ERROR:
+                    // compare current date and next retry date.
+                    if (getTimer() >= _keyLoadErrorDate) {
+                        /* try to reload the key ...
+                        calling _loadfragment will also reload key */
+                        _loadfragment(_fragCurrent);
+                        _loadingState = LOADING_IN_PROGRESS;
+                    }
+                    break;
+                // if fragment loading failed
+                case LOADING_FRAGMENT_IO_ERROR:
+                    // compare current date and next retry date.
+                    if (getTimer() >= _fragLoadErrorDate) {
+                        /* try to reload fragment ... */
+                        _loadfragment(_fragCurrent);
+                        _loadingState = LOADING_IN_PROGRESS;
+                    }
+                    break;
+                case LOADING_COMPLETED:
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LAST_VOD_FRAGMENT_LOADED));
+                    // stop fragment loader, as no other fragments can be loaded
+                    stop();
+                    break;
+                default:
+                    CONFIG::LOGGING {
+                        Log.error("invalid loading state:" + _loadingState);
+                    }
+                    break;
+            }
+        }
+
+        public function seek(position : Number) : void {
+            CONFIG::LOGGING {
+                Log.debug("FragmentLoader:seek(" + position.toFixed(2) + ")");
+            }
+            // reset IO Error when seeking
+            _fragRetryCount = _keyRetryCount = 0;
+            _fragRetryTimeout = _keyRetryTimeout = 1000;
+            _loadingState = LOADING_IDLE;
+            _seekPosition = position;
+            _fragmentFirstLoaded = false;
+            _fragPrevious = null;
+            _fragSkipping = false;
+            _fragSkipCount = 0;
+            _levelNext = -1;
+            _timer.start();
+        }
+
+        public function seekFromLastFrag(lastFrag : Fragment) : void {
+            CONFIG::LOGGING {
+                Log.info("FragmentLoader:seekFromLastFrag(level:" + lastFrag.level + ",SN:" + lastFrag.seqnum + ",PTS:" + lastFrag.data.pts_start +")");
+            }
+            // reset IO Error when seeking
+            _fragRetryCount = _keyRetryCount = 0;
+            _fragRetryTimeout = _keyRetryTimeout = 1000;
+            _loadingState = LOADING_IDLE;
+            _fragmentFirstLoaded = true;
+            _fragSkipping = false;
+            _fragSkipCount = 0;
+            _levelNext = -1;
+            _fragPrevious = lastFrag;
+            _timer.start();
+        }
+
+        /** key load completed. **/
+        private function _keyLoadCompleteHandler(event : Event) : void {
+            if (_loadingState == LOADING_IDLE)
+                return;
+            CONFIG::LOGGING {
+                Log.debug("key loading completed");
+            }
+            var hlsError : HLSError;
+            // Collect key data
+            if ( _keystreamloader.bytesAvailable == 16 ) {
+                // load complete, reset retry counter
+                _keyRetryCount = 0;
+                _keyRetryTimeout = 1000;
+                var keyData : ByteArray = new ByteArray();
+                _keystreamloader.readBytes(keyData, 0, 0);
+                _keymap[_fragCurrent.decrypt_url] = keyData;
+                // now load fragment
+                try {
+                    CONFIG::LOGGING {
+                        Log.debug("loading fragment:" + _fragCurrent.url);
+                    }
+                    _fragCurrent.data.bytes = null;
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_LOADING, _fragCurrent.url));
+                    _fragstreamloader.load(new URLRequest(_fragCurrent.url));
+                } catch (error : Error) {
+                    hlsError = new HLSError(HLSError.FRAGMENT_LOADING_ERROR, _fragCurrent.url, error.message);
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+                }
+            } else {
+                hlsError = new HLSError(HLSError.KEY_PARSING_ERROR, _fragCurrent.decrypt_url, "invalid key size: received " + _keystreamloader.bytesAvailable + " / expected 16 bytes");
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+            }
+        };
+
+        private function _keyLoadHTTPStatusHandler(event : HTTPStatusEvent) : void {
+            _keyLoadStatus = event.status;
+        }
+
+        private function _keyhandleIOError(message : String) : void {
+            CONFIG::LOGGING {
+                Log.error("I/O Error while loading key:" + message);
+            }
+            if (HLSSettings.keyLoadMaxRetry == -1 || _keyRetryCount < HLSSettings.keyLoadMaxRetry) {
+                _loadingState = LOADING_KEY_IO_ERROR;
+                _keyLoadErrorDate = getTimer() + _keyRetryTimeout;
+                CONFIG::LOGGING {
+                    Log.warn("retry key load in " + _keyRetryTimeout + " ms, count=" + _keyRetryCount);
+                }
+                /* exponential increase of retry timeout, capped to keyLoadMaxRetryTimeout */
+                _keyRetryCount++;
+                _keyRetryTimeout = Math.min(HLSSettings.keyLoadMaxRetryTimeout, 2 * _keyRetryTimeout);
+            } else {
+                var hlsError : HLSError = new HLSError(HLSError.KEY_LOADING_ERROR, _fragCurrent.decrypt_url, "I/O Error :" + message);
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+            }
+        }
+
+        /* in case of parsing error,
+            first, try to flush any tags that might have been injected in the NetStream
+            then switch to redundant stream if any
+            OR level switch down and cap level if in auto mode
+            OR skip fragment if allowed to
+            if not allowed to, report PARSING error
+        */
+        private function _fragHandleParsingError(message : String) : void {
+            var hlsError : HLSError = new HLSError(HLSError.FRAGMENT_PARSING_ERROR, _fragCurrent.url, "Parsing Error :" + message);
+            var level : Level = _levels[_fragCurrent.level];
+            CONFIG::LOGGING {
+                Log.warn(hlsError.msg);
+            }
+            // flush any tags that might have been injected for this fragment
+            _streamBuffer.flushLastFragment(_fragCurrent.level,_fragCurrent.seqnum);
+            _hls.dispatchEvent(new HLSEvent(HLSEvent.WARNING, hlsError));
+            // if we have redundant streams left for that level, switch to it
+            if(level.redundantStreamId < level.redundantStreamsNb) {
+                CONFIG::LOGGING {
+                    Log.warn("parsing error, switch to redundant stream");
+                }
+                level.redundantStreamId++;
+                _fragRetryCount = 0;
+                _fragRetryTimeout = 1000;
+                _loadingState = LOADING_IDLE;
+                // dispatch event to force redundant level loading
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_SWITCH, _fragCurrent.level));
+            } else if(_hls.autoLevel && _fragCurrent.level) {
+                // auto level and not on level 0, try to switch down for next fragment, and cap level to avoid coming back on this one
+                _levelNext = _fragCurrent.level-1;
+                if(_hls.autoLevelCapping == -1) {
+                    _hls.autoLevelCapping = _levelNext;
+                } else {
+                    _hls.autoLevelCapping = Math.min(_levelNext,_hls.autoLevelCapping);
+                }
+                // switch back to IDLE state to request new fragment at lowest level
+                _loadingState = LOADING_IDLE;
+            } else if(HLSSettings.fragmentLoadSkipAfterMaxRetry == true && _fragSkipCount < HLSSettings.maxSkippedFragments  || HLSSettings.maxSkippedFragments < 0) {
+                CONFIG::LOGGING {
+                    Log.warn("error parsing fragment, skip it and load next one");
+                }
+                var tags : Vector.<FLVTag> = tags = new Vector.<FLVTag>();
+                tags.push(_fragCurrent.getSkippedTag());
+                // send skipped FLV tag to StreamBuffer
+                _streamBuffer.appendTags(HLSLoaderTypes.FRAGMENT_MAIN,_fragCurrent.level,_fragCurrent.seqnum ,tags,_fragCurrent.data.pts_start_computed, _fragCurrent.data.pts_start_computed + 1000*_fragCurrent.duration, _fragCurrent.continuity, _fragCurrent.start_time);
+                _fragRetryCount = 0;
+                _fragRetryTimeout = 1000;
+                _fragPrevious = _fragCurrent;
+                _fragSkipping = true;
+                _fragSkipCount++;
+                CONFIG::LOGGING {
+                    Log.debug("fragments skipped / max: " + _fragSkipCount + "/" + HLSSettings.maxSkippedFragments );
+                }
+                // set fragment first loaded to be true to ensure that we can skip first fragment as well
+                _fragmentFirstLoaded = true;
+                _loadingState = LOADING_IDLE;
+            } else {
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+            }
+        }
+
+        /* in case of IO error,
+            retry loading fragment several times if allowed to
+            then switch to redundant stream if any
+            OR level switch down and cap level if in auto mode
+            OR skip fragment if allowed to
+            if not allowed to, report LOADING error
+        */
+        private function _fraghandleIOError(message : String) : void {
+            var hlsError : HLSError = new HLSError(HLSError.FRAGMENT_LOADING_ERROR, _fragCurrent.url, "I/O Error while loading fragment:" + message);
+            _hls.dispatchEvent(new HLSEvent(HLSEvent.WARNING, hlsError));
+            CONFIG::LOGGING {
+                Log.warn(hlsError.msg);
+            }
+            if (HLSSettings.fragmentLoadMaxRetry == -1 || _fragRetryCount < HLSSettings.fragmentLoadMaxRetry) {
+                _loadingState = LOADING_FRAGMENT_IO_ERROR;
+                _fragLoadErrorDate = getTimer() + _fragRetryTimeout;
+                CONFIG::LOGGING {
+                    Log.warn("retry fragment load in " + _fragRetryTimeout + " ms, count=" + _fragRetryCount);
+                }
+                /* exponential increase of retry timeout, capped to fragmentLoadMaxRetryTimeout */
+                _fragRetryCount++;
+                _fragRetryTimeout = Math.min(HLSSettings.fragmentLoadMaxRetryTimeout, 2 * _fragRetryTimeout);
+            } else {
+                var level : Level = _levels[_fragCurrent.level];
+                // if we have redundant streams left for that level, switch to it
+                if(level.redundantStreamId < level.redundantStreamsNb) {
+                    CONFIG::LOGGING {
+                        Log.warn("max load retry reached, switch to redundant stream");
+                    }
+                    level.redundantStreamId++;
+                    _fragRetryCount = 0;
+                    _fragRetryTimeout = 1000;
+                    _loadingState = LOADING_IDLE;
+                    // dispatch event to force redundant level loading
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_SWITCH, _fragCurrent.level));
+                } else if(_hls.autoLevel && _fragCurrent.level) {
+                    // auto level and not on level 0, try to switch down for next fragment, and cap level to avoid coming back on this one
+                    _levelNext = _fragCurrent.level-1;
+                    if(_hls.autoLevelCapping == -1) {
+                        _hls.autoLevelCapping = _levelNext;
+                    } else {
+                        _hls.autoLevelCapping = Math.min(_levelNext,_hls.autoLevelCapping);
+                    }
+                    // switch back to IDLE state to request new fragment at lowest level
+                    _loadingState = LOADING_IDLE;
+                } else if(HLSSettings.fragmentLoadSkipAfterMaxRetry == true && _fragSkipCount < HLSSettings.maxSkippedFragments || HLSSettings.maxSkippedFragments < 0) {
+                    /* check if loaded fragment is not the last one of a live playlist.
+                        if it is the case, don't skip to next, as there is no next fragment :-)
+                    */
+                    if(_hls.type == HLSTypes.LIVE && _fragCurrent.seqnum == level.end_seqnum) {
+                        _loadingState = LOADING_FRAGMENT_IO_ERROR;
+                        _fragLoadErrorDate = getTimer() + _fragRetryTimeout;
+                        CONFIG::LOGGING {
+                            Log.warn("max load retry reached on last fragment of live playlist, retrying loading this one...");
+                        }
+                        /* exponential increase of retry timeout, capped to fragmentLoadMaxRetryTimeout */
+                        _fragRetryCount++;
+                        _fragRetryTimeout = Math.min(HLSSettings.fragmentLoadMaxRetryTimeout, 2 * _fragRetryTimeout);
+                    } else {
+                        CONFIG::LOGGING {
+                            Log.warn("max fragment load retry reached, skip fragment and load next one.");
+                        }
+                        var tags : Vector.<FLVTag> = tags = new Vector.<FLVTag>();
+                        tags.push(_fragCurrent.getSkippedTag());
+                        // send skipped FLV tag to StreamBuffer
+                        _streamBuffer.appendTags(HLSLoaderTypes.FRAGMENT_MAIN,_fragCurrent.level,_fragCurrent.seqnum ,tags,_fragCurrent.data.pts_start_computed, _fragCurrent.data.pts_start_computed + 1000*_fragCurrent.duration, _fragCurrent.continuity, _fragCurrent.start_time);
+                        _fragRetryCount = 0;
+                        _fragRetryTimeout = 1000;
+                        _fragPrevious = _fragCurrent;
+                        _fragSkipping = true;
+                        _fragSkipCount++;
+                        CONFIG::LOGGING {
+                            Log.debug("fragments skipped / max: " + _fragSkipCount + "/" + HLSSettings.maxSkippedFragments );
+                        }
+                        // set fragment first loaded to be true to ensure that we can skip first fragment as well
+                        _fragmentFirstLoaded = true;
+                        _loadingState = LOADING_IDLE;
+                    }
+                } else {
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+                }
+            }
+        }
+
+        private function _fragLoadHTTPStatusHandler(event : HTTPStatusEvent) : void {
+            _fragLoadStatus = event.status;
+        }
+
+        private function _fragLoadProgressHandler(event : ProgressEvent) : void {
+            var fragData : FragmentData = _fragCurrent.data;
+            if (fragData.bytes == null) {
+                fragData.bytes = new ByteArray();
+                fragData.bytesLoaded = 0;
+                fragData.flushTags();
+                _metrics.loading_begin_time = getTimer();
+
+                // decrypt data if needed
+                if (_fragCurrent.decrypt_url != null) {
+                    _metrics.decryption_begin_time = getTimer();
+                    fragData.decryptAES = new AES(_hls.stage, _keymap[_fragCurrent.decrypt_url], _fragCurrent.decrypt_iv, _fragDecryptProgressHandler, _fragDecryptCompleteHandler);
+                    CONFIG::LOGGING {
+                        Log.debug("init AES context");
+                    }
+                } else {
+                    fragData.decryptAES = null;
+                }
+            }
+            if (event.bytesLoaded > fragData.bytesLoaded
+                && _fragstreamloader.bytesAvailable > 0) {  // prevent EOF error race condition
+                var data : ByteArray = new ByteArray();
+                _fragstreamloader.readBytes(data);
+                fragData.bytesLoaded += data.length;
+                // CONFIG::LOGGING {
+                // Log.debug2("bytesLoaded/bytesTotal:" + event.bytesLoaded + "/" + event.bytesTotal);
+                // }
+                if (fragData.decryptAES != null) {
+                    fragData.decryptAES.append(data);
+                } else {
+                    _fragDecryptProgressHandler(data);
+                }
+            }
+        }
+
+        /** frag load completed. **/
+        private function _fragLoadCompleteHandler(event : Event) : void {
+            var fragData : FragmentData = _fragCurrent.data;
+            if (fragData.bytes == null) {
+                CONFIG::LOGGING {
+                    Log.warn("fragment size is null, invalid it and load next one");
+                }
+                _levels[_hls.loadLevel].updateFragment(_fragCurrent.seqnum, false);
+                _loadingState = LOADING_IDLE;
+                return;
+            }
+            CONFIG::LOGGING {
+                Log.debug("loading completed");
+            }
+            _fragSkipping = false;
+            _fragSkipCount = 0;
+            _metrics.loading_end_time = getTimer();
+            _metrics.size = fragData.bytesLoaded;
+
+            var _loading_duration : uint = _metrics.loading_end_time - _metrics.loading_request_time;
+            CONFIG::LOGGING {
+                Log.debug("Loading       duration/RTT/length/speed:" + _loading_duration + "/" + (_metrics.loading_begin_time - _metrics.loading_request_time) + "/" + _metrics.size + "/" + Math.round((8000 * _metrics.size / _loading_duration) / 1024) + " kb/s");
+            }
+            if (fragData.decryptAES) {
+                fragData.decryptAES.notifycomplete();
+            } else {
+                _fragDecryptCompleteHandler();
+            }
+        }
+
+        private function _fragDecryptProgressHandler(data : ByteArray) : void {
+            data.position = 0;
+            var fragData : FragmentData = _fragCurrent.data;
+            if (_metrics.parsing_begin_time ==0) {
+                _metrics.parsing_begin_time = getTimer();
+            }
+            var bytes : ByteArray = fragData.bytes;
+            if (_fragCurrent.byterange_start_offset != -1) {
+                bytes.position = bytes.length;
+                bytes.writeBytes(data);
+                // if we have retrieved all the data, disconnect loader and notify fragment complete
+                if (bytes.length >= _fragCurrent.byterange_end_offset) {
+                    if (_fragstreamloader.connected) {
+                        _fragstreamloader.close();
+                        _fragLoadCompleteHandler(null);
+                    }
+                }
+                /* dont do progressive parsing of segment with byte range option */
+                return;
+            }
+            if (_demux == null) {
+                /* probe file type */
+                bytes.position = bytes.length;
+                bytes.writeBytes(data);
+                data = bytes;
+                _demux = DemuxHelper.probe(data, _levels[_hls.loadLevel], _fragParsingAudioSelectionHandler, _fragParsingProgressHandler, _fragParsingCompleteHandler, _fragParsingErrorHandler, _fragParsingVideoMetadataHandler, _fragParsingID3TagHandler, false);
+            }
+            if (_demux) {
+                _demux.append(data);
+            }
+        }
+
+        private function _fragDecryptCompleteHandler() : void {
+            if (_loadingState == LOADING_IDLE)
+                return;
+            var fragData : FragmentData = _fragCurrent.data;
+
+            if (fragData.decryptAES) {
+                _metrics.decryption_end_time = getTimer();
+                var decrypt_duration : Number = _metrics.decryption_end_time - _metrics.decryption_begin_time;
+                CONFIG::LOGGING {
+                    Log.debug("Decrypted     duration/length/speed:" + decrypt_duration + "/" + fragData.bytesLoaded + "/" + Math.round((8000 * fragData.bytesLoaded / decrypt_duration) / 1024) + " kb/s");
+                }
+                fragData.decryptAES = null;
+            }
+
+            // deal with byte range here
+            if (_fragCurrent.byterange_start_offset != -1) {
+                CONFIG::LOGGING {
+                    Log.debug("trim byte range, start/end offset:" + _fragCurrent.byterange_start_offset + "/" + _fragCurrent.byterange_end_offset);
+                }
+                var bytes : ByteArray = new ByteArray();
+                fragData.bytes.position = _fragCurrent.byterange_start_offset;
+                fragData.bytes.readBytes(bytes, 0, _fragCurrent.byterange_end_offset - _fragCurrent.byterange_start_offset);
+                _demux = DemuxHelper.probe(bytes, _levels[_hls.loadLevel], _fragParsingAudioSelectionHandler, _fragParsingProgressHandler, _fragParsingCompleteHandler, _fragParsingErrorHandler, _fragParsingVideoMetadataHandler, _fragParsingID3TagHandler, false);
+                if (_demux) {
+                    bytes.position = 0;
+                    _demux.append(bytes);
+                }
+            }
+
+            if (_demux == null) {
+                CONFIG::LOGGING {
+                    Log.error("unknown fragment type");
+                    if (HLSSettings.logDebug2) {
+                        fragData.bytes.position = 0;
+                        var bytes2 : ByteArray = new ByteArray();
+                        fragData.bytes.readBytes(bytes2, 0, 512);
+                        Log.debug2("frag dump(512 bytes)");
+                        Log.debug2(Hex.fromArray(bytes2));
+                    }
+                }
+                // invalid fragment
+                _fraghandleIOError("invalid content received");
+                fragData.bytes = null;
+                return;
+            }
+            fragData.bytes = null;
+            _demux.notifycomplete();
+        }
+
+        /** stop loading fragment **/
+        public function stop() : void {
+            _stop_load();
+            _timer.stop();
+            _loadingState = LOADING_STOPPED;
+        }
+
+        private function _stop_load() : void {
+            if (_fragstreamloader && _fragstreamloader.connected) {
+                _fragstreamloader.close();
+            }
+            if (_keystreamloader && _keystreamloader.connected) {
+                _keystreamloader.close();
+            }
+
+            if (_demux) {
+                _demux.cancel();
+            }
+
+            if (_fragCurrent) {
+                var fragData : FragmentData = _fragCurrent.data;
+                if (fragData.decryptAES) {
+                    fragData.decryptAES.cancel();
+                    fragData.decryptAES = null;
+                }
+                fragData.bytes = null;
+            }
+        }
+
+        /** Catch IO and security errors. **/
+        private function _keyLoadErrorHandler(event : ErrorEvent) : void {
+            var txt : String;
+            var code : int;
+            if (event is SecurityErrorEvent) {
+                txt = "Cannot load key: crossdomain access denied:" + event.text;
+                code = HLSError.KEY_LOADING_CROSSDOMAIN_ERROR;
+            } else {
+                _keyhandleIOError("HTTP status:" + _keyLoadStatus + ",msg:" + event.text);
+            }
+        };
+
+        /** Catch IO and security errors. **/
+        private function _fragLoadErrorHandler(event : ErrorEvent) : void {
+            if (event is SecurityErrorEvent) {
+                var txt : String = "Cannot load fragment: crossdomain access denied:" + event.text;
+                var hlsError : HLSError = new HLSError(HLSError.FRAGMENT_LOADING_CROSSDOMAIN_ERROR, _fragCurrent.url, txt);
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+            } else {
+                if(_fragLoadStatus == 200) {
+                    _fragHandleParsingError("HTTP 2OO but IO error, treat as parsing error");
+                } else {
+                    _fraghandleIOError("HTTP status:" + _fragLoadStatus + ",msg:" + event.text);
+                }
+            }
+        };
+
+        private function _loadfirstfragment(position : Number, level : int) : int {
+            CONFIG::LOGGING {
+                Log.debug("loadfirstfragment(" + position + ")");
+            }
+            var frag : Fragment = _levels[level].getFragmentBeforePosition(position);
+            _hasDiscontinuity = true;
+            CONFIG::LOGGING {
+                Log.debug("Loading       " + frag.seqnum + " of [" + (_levels[level].start_seqnum) + "," + (_levels[level].end_seqnum) + "],level " + level);
+            }
+            _loadfragment(frag);
+            return LOADING_IN_PROGRESS;
+        }
+
+        /** Load a fragment **/
+        private function _loadnextfragment(level : int, frag_previous : Fragment) : int {
+            CONFIG::LOGGING {
+                Log.debug("loadnextfragment()");
+            }
+            var new_seqnum : Number;
+            var last_seqnum : Number = -1;
+            var levelObj : Level = _levels[level];
+            var log_prefix : String;
+            var frag : Fragment;
+
+            if (_switchLevel == false || frag_previous.continuity == -1) {
+                last_seqnum = frag_previous.seqnum;
+            } else {
+                // level switch
+                // trust program-time : if program-time defined in previous loaded fragment, try to find seqnum matching program-time in new level.
+                if (frag_previous.program_date) {
+                    last_seqnum = levelObj.getSeqNumNearestProgramDate(frag_previous.program_date);
+                    CONFIG::LOGGING {
+                        Log.debug("loadnextfragment : getSeqNumNearestProgramDate(level,date,cc:" + level + "," + frag_previous.program_date + ")=" + last_seqnum);
+                    }
+                }
+                if (last_seqnum == -1) {
+                    // if we are here, it means that no program date info is available in the playlist. try to get last seqnum position from PTS + continuity counter
+                    // last_pts is an approximation of last injected PTS of previous fragment
+                    var last_pts : Number = frag_previous.data.pts_start_computed+1000*frag_previous.duration;
+                    last_seqnum = levelObj.getSeqNumNearestPTS(last_pts, frag_previous.continuity);
+                    CONFIG::LOGGING {
+                        Log.debug("loadnextfragment : getSeqNumNearestPTS(level,pts,cc:" + level + "," + last_pts + "," + frag_previous.continuity + ")=" + last_seqnum);
+                    }
+                    last_seqnum--;
+                    if (last_seqnum == Number.POSITIVE_INFINITY) {
+                        /* requested PTS above max PTS of this level:
+                         * this case could happen when loading is completed
+                         * or when switching level at the edge of live playlist,
+                         * in case playlist of new level is outdated
+                         */
+                        if (_hls.type == HLSTypes.VOD) {
+                            // if VOD playlist, loading is completed
+                            return LOADING_COMPLETED;
+                        } else {
+                            // if live playlist, loading is pending on manifest update
+                            return LOADING_WAITING_LEVEL_UPDATE;
+                        }
+                    } else if (last_seqnum < -1) {
+                        // if we are here, it means that we have no PTS info for this continuity index, we need to do some PTS probing to find the right seqnum
+                        /* we need to perform PTS analysis on fragments from same continuity range
+                        get first fragment from playlist matching with criteria and load pts */
+                        last_seqnum = levelObj.getFirstSeqNumfromContinuity(frag_previous.continuity);
+                        CONFIG::LOGGING {
+                            Log.debug("loadnextfragment : getFirstSeqNumfromContinuity(level,cc:" + level + "," + frag_previous.continuity + ")=" + last_seqnum);
+                        }
+                        if (last_seqnum == Number.NEGATIVE_INFINITY) {
+                            // playlist not yet received
+                            return LOADING_WAITING_LEVEL_UPDATE;
+                        }
+                        /* when probing PTS, take previous sequence number as reference if possible */
+                        new_seqnum = Math.min(frag_previous.seqnum + 1, levelObj.getLastSeqNumfromContinuity(frag_previous.continuity));
+                        new_seqnum = Math.max(new_seqnum, levelObj.getFirstSeqNumfromContinuity(frag_previous.continuity));
+                        _ptsAnalyzing = true;
+                        log_prefix = "analyzing PTS ";
+                    } else {
+                        // last seqnum found on new level, reset PTS analysis flag
+                        _ptsAnalyzing = false;
+                    }
+                }
+            }
+
+            if (_ptsAnalyzing == false) {
+                if (last_seqnum == levelObj.end_seqnum) {
+                    // if last segment of level already loaded, return
+                    if (_hls.type == HLSTypes.VOD) {
+                        // if VOD playlist, loading is completed
+                        return LOADING_COMPLETED;
+                    } else {
+                        // if live playlist, loading is pending on manifest update
+                        return LOADING_WAITING_LEVEL_UPDATE;
+                    }
+                } else {
+                    // if previous segment is not the last one, increment it to get new seqnum
+                    new_seqnum = last_seqnum + 1;
+                    if (new_seqnum < levelObj.start_seqnum) {
+                        // loading stalled ! report to caller
+                        return LOADING_STALLED;
+                    }
+                    frag = levelObj.getFragmentfromSeqNum(new_seqnum);
+                    if (frag == null) {
+                        CONFIG::LOGGING {
+                            Log.warn("error trying to load " + new_seqnum + " of [" + (levelObj.start_seqnum) + "," + (levelObj.end_seqnum) + "],level " + level);
+                        }
+                        return LOADING_WAITING_LEVEL_UPDATE;
+                    }
+                    // check whether there is a discontinuity between last segment and new segment
+                    _hasDiscontinuity = ((frag.continuity != frag_previous.continuity) || _fragSkipping);
+                    ;
+                    log_prefix = "Loading       ";
+                }
+            }
+            frag = levelObj.getFragmentfromSeqNum(new_seqnum);
+            CONFIG::LOGGING {
+                Log.debug(log_prefix + new_seqnum + " of [" + (levelObj.start_seqnum) + "," + (levelObj.end_seqnum) + "],level " + level);
+            }
+            _loadfragment(frag);
+            return LOADING_IN_PROGRESS;
+        };
+
+        private function _loadfragment(frag : Fragment) : void {
+            // postpone URLStream init before loading first fragment
+            if (_fragstreamloader == null) {
+                var urlStreamClass : Class = _hls.URLstream as Class;
+                _fragstreamloader = new JSURLStream();
+                _fragstreamloader.addEventListener(IOErrorEvent.IO_ERROR, _fragLoadErrorHandler);
+                _fragstreamloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _fragLoadErrorHandler);
+                _fragstreamloader.addEventListener(ProgressEvent.PROGRESS, _fragLoadProgressHandler);
+                _fragstreamloader.addEventListener(HTTPStatusEvent.HTTP_STATUS, _fragLoadHTTPStatusHandler);
+                _fragstreamloader.addEventListener(Event.COMPLETE, _fragLoadCompleteHandler);
+                _keystreamloader = (new urlStreamClass()) as URLStream;
+                _keystreamloader.addEventListener(IOErrorEvent.IO_ERROR, _keyLoadErrorHandler);
+                _keystreamloader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _keyLoadErrorHandler);
+                _keystreamloader.addEventListener(HTTPStatusEvent.HTTP_STATUS, _keyLoadHTTPStatusHandler);
+                _keystreamloader.addEventListener(Event.COMPLETE, _keyLoadCompleteHandler);
+            }
+            if (_hasDiscontinuity || _switchLevel) {
+                _demux = null;
+            }
+            _metrics = new HLSLoadMetrics(HLSLoaderTypes.FRAGMENT_MAIN);
+            _metrics.level = frag.level;
+            _metrics.id = frag.seqnum;
+            _metrics.loading_request_time = getTimer();
+            _fragCurrent = frag;
+            frag.data.auto_level = _hls.autoLevel;
+            if (frag.decrypt_url != null) {
+                if (_keymap[frag.decrypt_url] == undefined) {
+                    // load key
+                    CONFIG::LOGGING {
+                        Log.debug("loading key:" + frag.decrypt_url);
+                    }
+                    _keystreamloader.load(new URLRequest(frag.decrypt_url));
+                    return;
+                }
+            }
+            try {
+                frag.data.bytes = null;
+                CONFIG::LOGGING {
+                    Log.debug("loading fragment:" + frag.url);
+                }
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_LOADING, frag.url));
+                _fragstreamloader.load(new URLRequest(frag.url));
+            } catch (error : Error) {
+                var hlsError : HLSError = new HLSError(HLSError.FRAGMENT_LOADING_ERROR, frag.url, error.message);
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+            }
+        }
+
+        /** Store the manifest data. **/
+        private function _manifestLoadedHandler(event : HLSEvent) : void {
+            _levels = event.levels;
+            _manifestJustLoaded = true;
+        };
+
+        /** Store the manifest data. **/
+        private function _levelLoadedHandler(event : HLSEvent) : void {
+            _levelLastLoaded = event.loadMetrics.level;
+            if (_loadingState == LOADING_WAITING_LEVEL_UPDATE && _levelLastLoaded == _hls.loadLevel) {
+                _loadingState = LOADING_IDLE;
+            }
+            // speed up loading of new fragment
+            _timer.start();
+        };
+
+        /** Store the manifest data. **/
+        private function _levelLoadingAbortedHandler(event : HLSEvent) : void {
+            _levelNext = event.level-1;
+            CONFIG::LOGGING {
+                Log.warn("FragmentLoader:_levelLoadingAbortedHandler:switch down to:" + _levelNext);
+            }
+            _loadingState = LOADING_IDLE;
+            // speed up loading of new playlist/fragment
+            _timer.start();
+        };
+
+        private function _fragParsingErrorHandler(error : String) : void {
+            // abort any load in progress
+            _stop_load();
+            // then try to overcome parsing error
+            _fragHandleParsingError(error);
+        }
+
+        private function _fragParsingID3TagHandler(id3_tags : Vector.<ID3Tag>) : void {
+            _fragCurrent.data.id3_tags = id3_tags;
+        }
+
+        /** triggered by demux, it should return the audio track to be parsed */
+        private function _fragParsingAudioSelectionHandler(audioTrackList : Vector.<AudioTrack>) : AudioTrack {
+            return _audioTrackController.audioTrackSelectionHandler(audioTrackList);
+        }
+
+        /** triggered by demux, it should return video width/height */
+        private function _fragParsingVideoMetadataHandler(width : uint, height : uint) : void {
+            var fragData : FragmentData = _fragCurrent.data;
+            if (fragData.video_width == 0) {
+                CONFIG::LOGGING {
+                    Log.debug("AVC: width/height:" + width + "/" + height);
+                }
+                fragData.video_width = width;
+                fragData.video_height = height;
+            }
+        }
+
+        /** triggered when demux has retrieved some tags from fragment **/
+        private function _fragParsingProgressHandler(tags : Vector.<FLVTag>) : void {
+            CONFIG::LOGGING {
+                Log.debug2(tags.length + " tags extracted");
+            }
+            var fragData : FragmentData = _fragCurrent.data;
+            fragData.appendTags(tags);
+
+            /* try to do progressive buffering here.
+             * only do it in case :
+             *      first fragment is already loaded
+             *      or if first fragment is not loaded, we can do it if
+             *          startLevel is already defined (startLevel is already set or
+             *          startFromLevel/startFromBitrate not set to -1
+             *          or we only have one quality level
+             *      in the other cases, flashls will first download one fragment at level 0 to measure dl bw, used to assess start level ...)
+             */
+            if (( !_manifestJustLoaded ||
+                (_levelController.isStartLevelSet() ||
+                 HLSSettings.startFromLevel !== -1 ||
+                 HLSSettings.startFromBitrate !== -1 ||
+                 _levels.length == 1))) {
+                /* if audio expected, PTS analysis is done on audio
+                 * if audio not expected, PTS analysis is done on video
+                 * the check below ensures that we can compute min/max PTS
+                 */
+                if ((_demux.audioExpected && fragData.audio_found) || (!_demux.audioExpected && fragData.video_found)) {
+                    if (_ptsAnalyzing == true) {
+                        var levelObj : Level = _levels[_hls.loadLevel];
+                        _ptsAnalyzing = false;
+                        levelObj.updateFragment(_fragCurrent.seqnum, true, fragData.pts_min, fragData.pts_min + _fragCurrent.duration * 1000);
+                        /* in case we are probing PTS, retrieve PTS info and synchronize playlist PTS / sequence number */
+                        CONFIG::LOGGING {
+                            Log.debug("analyzed PTS " + _fragCurrent.seqnum + " of [" + (levelObj.start_seqnum) + "," + (levelObj.end_seqnum) + "],level " + _hls.loadLevel + " m PTS:" + fragData.pts_min);
+                        }
+                        /* check if fragment loaded for PTS analysis is the next one
+                        if this is the expected one, then continue
+                        if not, then cancel current fragment loading, next call to loadnextfragment() will load the right seqnum
+                         */
+                        var next_pts:Number;
+                        var next_seqnum:Number;
+
+                        // Resolves intermittent issue that causes the player to crash due to missing previous fragment data while seeking
+                        if (_fragPrevious && _fragPrevious.data) {
+                            next_pts = _fragPrevious.data.pts_start_computed + 1000*_fragPrevious.duration;
+                            next_seqnum = levelObj.getSeqNumNearestPTS(next_pts, _fragCurrent.continuity);
+                        } else {
+                            CONFIG::LOGGING {
+                                Log.debug("Previous fragment data not found while analyzing PTS!");
+                            }
+                        }
+
+                        CONFIG::LOGGING {
+                            Log.debug("analyzed PTS : getSeqNumNearestPTS(level,pts,cc:" + _hls.loadLevel + "," + next_pts + "," + _fragCurrent.continuity + ")=" + next_seqnum);
+                        }
+                        // CONFIG::LOGGING {
+                        // Log.info("seq/next:"+ _seqnum+"/"+ next_seqnum);
+                        // }
+                        if (next_seqnum !== _fragCurrent.seqnum) {
+                            // stick to same level after PTS analysis
+                            _levelNext = _hls.loadLevel;
+                            CONFIG::LOGGING {
+                                Log.debug("PTS analysis done on " + _fragCurrent.seqnum + ", matching seqnum is " + next_seqnum + " of [" + (levelObj.start_seqnum) + "," + (levelObj.end_seqnum) + "],cancel loading and get new one");
+                            }
+                            // cancel loading
+                            _stop_load();
+                            // clean-up tags
+                            fragData.flushTags();
+                            // tell that new fragment could be loaded
+                            _loadingState = LOADING_IDLE;
+                            return;
+                        }
+                    }
+                    if (fragData.metadata_tag_injected == false) {
+                        fragData.tags.unshift(_fragCurrent.getMetadataTag());
+                        if (_hasDiscontinuity) {
+                            fragData.tags.unshift(new FLVTag(FLVTag.DISCONTINUITY, fragData.dts_min, fragData.dts_min, false));
+                        }
+                        fragData.metadata_tag_injected = true;
+                    }
+                    // provide tags to StreamBuffer
+                    _streamBuffer.appendTags(HLSLoaderTypes.FRAGMENT_MAIN,_fragCurrent.level,_fragCurrent.seqnum , fragData.tags, fragData.tag_pts_min, fragData.tag_pts_max + fragData.tag_duration, _fragCurrent.continuity, _fragCurrent.start_time + fragData.tag_pts_start_offset / 1000);
+                    _metrics.parsing_end_time = getTimer();
+                    _metrics.size = fragData.bytesLoaded;
+                    _metrics.duration = fragData.tag_pts_end_offset;
+                    _metrics.id2 = fragData.tags.length;
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.TAGS_LOADED, _metrics));
+                    fragData.shiftTags();
+                    _hasDiscontinuity = false;
+                }
+            }
+        }
+
+        /** triggered when demux has completed fragment parsing **/
+        private function _fragParsingCompleteHandler() : void {
+            if (_loadingState == LOADING_IDLE)
+                return;
+            var hlsError : HLSError;
+            var fragData : FragmentData = _fragCurrent.data;
+            var fragLevelIdx : int = _fragCurrent.level;
+            if ((_demux.audioExpected && !fragData.audio_found) && (_demux.videoExpected && !fragData.video_found)) {
+                // handle it like a parsing error
+                _fragHandleParsingError("error parsing fragment, no tag found");
+                return;
+            }
+            // parsing complete, reset retry and skip counters
+            _fragRetryCount = 0;
+            _fragRetryTimeout = 1000;
+            _fragSkipCount = 0;
+            CONFIG::LOGGING {
+                if (fragData.audio_found) {
+                    Log.debug("m/M audio PTS:" + fragData.pts_min_audio + "/" + fragData.pts_max_audio);
+                }
+                if (fragData.video_found) {
+                    Log.debug("m/M video PTS:" + fragData.pts_min_video + "/" + fragData.pts_max_video);
+
+                    if (!fragData.audio_found) {
+                    } else {
+                        Log.debug("Delta audio/video m/M PTS:" + (fragData.pts_min_video - fragData.pts_min_audio) + "/" + (fragData.pts_max_video - fragData.pts_max_audio));
+                    }
+                }
+            }
+
+            // Calculate bandwidth
+            _metrics.parsing_end_time = getTimer();
+            CONFIG::LOGGING {
+                Log.debug("Total Process duration/length/bw:" + _metrics.processing_duration + "/" + _metrics.size + "/" + Math.round(_metrics.bandwidth / 1024) + " kb/s");
+            }
+
+            if (_manifestJustLoaded) {
+                _manifestJustLoaded = false;
+                if (HLSSettings.startFromLevel === -1 && HLSSettings.startFromBitrate === -1 && _levels.length > 1 && !_levelController.isStartLevelSet()) {
+                    // check if we can directly switch to a better bitrate, in case download bandwidth is enough
+                    var bestlevel : int = _levelController.getAutoStartBestLevel(_metrics.bandwidth,_metrics.processing_duration, 1000*_fragCurrent.duration);
+                    if (bestlevel > fragLevelIdx) {
+                        CONFIG::LOGGING {
+                            Log.info("enough download bandwidth, adjust start level from 0 to " + bestlevel);
+                        }
+                        // dispatch event for tracking purpose
+                        _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_LOADED, _metrics));
+                        // let's directly jump to the accurate level to improve quality at player start
+                        _levelNext = bestlevel;
+                        _loadingState = LOADING_IDLE;
+                        _switchLevel = true;
+                        _demux = null;
+                        _hls.dispatchEvent(new HLSEvent(HLSEvent.LEVEL_SWITCH, fragLevelIdx));
+                        // speed up loading of new playlist
+                        _timer.start();
+                        return;
+                    }
+                }
+            }
+
+            try {
+                _switchLevel = false;
+                _levelNext = -1;
+                var fragLevel : Level = _levels[fragLevelIdx];
+                CONFIG::LOGGING {
+                    Log.debug("Loaded        " + _fragCurrent.seqnum + " of [" + (fragLevel.start_seqnum) + "," + (fragLevel.end_seqnum) + "],level " + fragLevelIdx + " m/M PTS:" + fragData.pts_min + "/" + fragData.pts_max);
+                }
+                if (fragData.audio_found || fragData.video_found) {
+                    fragLevel.updateFragment(_fragCurrent.seqnum, true, fragData.pts_min, fragData.pts_max + fragData.tag_duration);
+                    // set pts_start here, it might not be updated directly in updateFragment() if this loaded fragment has been removed from a live playlist
+                    fragData.pts_start = fragData.pts_min;
+                    _hls.dispatchEvent(new HLSEvent(HLSEvent.PLAYLIST_DURATION_UPDATED, fragLevel.duration));
+                    if (fragData.tags.length) {
+                        if (fragData.metadata_tag_injected == false) {
+                            fragData.tags.unshift(_fragCurrent.getMetadataTag());
+                            if (_hasDiscontinuity) {
+                                fragData.tags.unshift(new FLVTag(FLVTag.DISCONTINUITY, fragData.dts_min, fragData.dts_min, false));
+                            }
+                            fragData.metadata_tag_injected = true;
+                        }
+                        _streamBuffer.appendTags(HLSLoaderTypes.FRAGMENT_MAIN, _fragCurrent.level,_fragCurrent.seqnum , fragData.tags, fragData.tag_pts_min, fragData.tag_pts_max + fragData.tag_duration, _fragCurrent.continuity, _fragCurrent.start_time + fragData.tag_pts_start_offset / 1000);
+                        _metrics.duration = fragData.pts_max + fragData.tag_duration - fragData.pts_min;
+                        _metrics.id2 = fragData.tags.length;
+                        _hls.dispatchEvent(new HLSEvent(HLSEvent.TAGS_LOADED, _metrics));
+                        fragData.shiftTags();
+                        _hasDiscontinuity = false;
+                    }
+                } else {
+                    _metrics.duration = _fragCurrent.duration * 1000;
+                }
+                _loadingState = LOADING_IDLE;
+                _ptsAnalyzing = false;
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.FRAGMENT_LOADED, _metrics));
+                _fragmentFirstLoaded = true;
+                _fragPrevious = _fragCurrent;
+            } catch (error : Error) {
+                hlsError = new HLSError(HLSError.OTHER_ERROR, _fragCurrent.url, error.message);
+                _hls.dispatchEvent(new HLSEvent(HLSEvent.ERROR, hlsError));
+            }
+            // speed up loading of new fragment
+            _timer.start();
+        }
+    }
+}


Add missing builtin flash.utils.ObjectOutput

error: pathspec 'src/flash/utils/ObjectOutput.as' did not match any file(s) known to git

--- src/flash/utils/ObjectOutput.as
+++ src/flash/utils/ObjectOutput.as
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package flash.utils {
+
+[native(cls='ObjectOutputClass')]
+internal class ObjectOutput implements IDataOutput {
+  public native function get objectEncoding(): uint;
+  public native function set objectEncoding(version: uint): void;
+  public native function get endian(): String;
+  public native function set endian(type: String): void;
+  public native function writeBytes(bytes: ByteArray, offset: uint = 0, length: uint = 0): void;
+  public native function writeBoolean(value: Boolean): void;
+  public native function writeByte(value: int): void;
+  public native function writeShort(value: int): void;
+  public native function writeInt(value: int): void;
+  public native function writeUnsignedInt(value: uint): void;
+  public native function writeFloat(value: Number): void;
+  public native function writeDouble(value: Number): void;
+  public native function writeMultiByte(value: String, charSet: String): void;
+  public native function writeUTF(value: String): void;
+  public native function writeUTFBytes(value: String): void;
+  public native function writeObject(object: *): void;
+}
+}