package com.kaltura.hls
{
	import com.kaltura.hls.m2ts.M2TSNetLoader;
	import com.kaltura.hls.manifest.HLSManifestParser;
	import com.kaltura.hls.manifest.HLSManifestPlaylist;
	import com.kaltura.hls.manifest.HLSManifestStream;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;

	import org.osmf.elements.VideoElement;
	import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait;
	import org.osmf.events.MediaError;
	import org.osmf.events.MediaErrorEvent;
	import org.osmf.media.DefaultMediaFactory;
	import org.osmf.media.MediaElement;
	import org.osmf.media.MediaResourceBase;
	import org.osmf.media.MediaTypeUtil;
	import org.osmf.media.URLResource;
	import org.osmf.metadata.Metadata;
	import org.osmf.metadata.MetadataNamespaces;
	import org.osmf.net.DynamicStreamingItem;
	import org.osmf.net.StreamType;
	import org.osmf.net.StreamingItem;
	import org.osmf.net.StreamingItemType;
	import org.osmf.traits.LoadState;
	import org.osmf.traits.LoadTrait;
	import org.osmf.traits.LoaderBase;
	import org.osmf.utils.URL;

	public class HLSLoader extends LoaderBase
	{
		protected var loadTrait:LoadTrait;
		protected var manifestLoader:URLLoader;
		protected var parser:HLSManifestParser;
		protected var factory:DefaultMediaFactory = new DefaultMediaFactory();
		private var supportedMimeTypes:Vector.<String> = new Vector.<String>();

		public function HLSLoader()
		{
			super();

			supportedMimeTypes.push( "application/x-mpegURL" );
			supportedMimeTypes.push( "application/vnd.apple.mpegURL" );
			supportedMimeTypes.push( "vnd.apple.mpegURL" );
			supportedMimeTypes.push( "video/MP2T" );
		}

		override protected function executeLoad(loadTrait:LoadTrait):void
		{
			this.loadTrait = loadTrait;

			updateLoadTrait(loadTrait, LoadState.LOADING);

			var url:String = URLResource(loadTrait.resource).url;
			manifestLoader = new URLLoader(new URLRequest(url));
			manifestLoader.addEventListener(Event.COMPLETE, onComplete);
			manifestLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			manifestLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
		}

		private function onComplete(event:Event):void
		{
			manifestLoader.removeEventListener(Event.COMPLETE, onComplete);
			manifestLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			manifestLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

			try {
				var resourceData:String = String((event.target as URLLoader).data);

				var url:String = URLResource(loadTrait.resource).url;

				// Start parsing the manifest.
				parser = new HLSManifestParser();
				parser.addEventListener(Event.COMPLETE, onManifestComplete);
				parser.parse(resourceData, url);
			}
			catch (parseError:Error)
			{
				updateLoadTrait(loadTrait, LoadState.LOAD_ERROR);
				loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(parseError.errorID, parseError.message)));
			}
		}

		protected function onManifestComplete(event:Event):void
		{
			trace("Manifest is loaded.");

			var isDVR:Boolean = false;

			// Construct the streaming resource and set it as our resource.
			var stream:HLSStreamingResource = new HLSStreamingResource(URLResource(loadTrait.resource).url, "", StreamType.DVR);
			stream.manifest = parser;

			var item:DynamicStreamingItem;
			var items:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();
			for(var i:int=0; i<parser.streams.length; i++)
			{
				var curStream:HLSManifestStream = parser.streams[i];

				//If there is more than one streaming quality available, check to see if filtering is required
				if (parser.streams.length > 1)
				{
					//Checks to see if there are min/max bitrate restrictions then throws out the outliers
					if(HLSManifestParser.MAX_BITRATE != -1 && curStream.bandwidth > HLSManifestParser.MAX_BITRATE ||
						HLSManifestParser.MIN_BITRATE != -1 && curStream.bandwidth < HLSManifestParser.MIN_BITRATE)
					{
						continue;
					}
				}

				item = new DynamicStreamingItem(curStream.uri, curStream.bandwidth, curStream.width, curStream.height);
				curStream.dynamicStream = item;
				items.push(item);
				if ( !(curStream.manifest ? curStream.manifest.streamEnds : false) ) isDVR = true;

				// Create dynamic streaming items for the backup streams
				if (!curStream.backupStream)
					continue;

				var mainStream:HLSManifestStream = curStream;
				curStream = curStream.backupStream;
				while (curStream != mainStream)
				{
					curStream.dynamicStream = new DynamicStreamingItem(curStream.uri, curStream.bandwidth, curStream.width, curStream.height);
					curStream = curStream.backupStream;
				}
			}

			// Deal with single rate M3Us by stuffing a single stream in.
			if(items.length == 0)
			{
				item = new DynamicStreamingItem(URLResource(loadTrait.resource).url, 0, 0, 0);
				items.push(item);

				// Also set the DVR state
				if ( !stream.manifest.streamEnds ) isDVR = true;
			}

			var alternateAudioItems:Vector.<StreamingItem> = new <StreamingItem>[];
			for ( var j:int = 0; j < parser.playLists.length; j++ )
			{
				var playList:HLSManifestPlaylist = parser.playLists[ j ];
				if ( !playList.manifest ) continue;
				var audioItem:StreamingItem = new StreamingItem( StreamingItemType.AUDIO, playList.name, 0, playList );
				alternateAudioItems.push( audioItem );
			}

			stream.streamItems = items;
			stream.alternativeAudioStreamItems = alternateAudioItems;

			var preferredIndex:int;

			//If there is only one stream quality (or less) sets default to first stream
			if (items.length <= 1)
			{
				preferredIndex = 0;
			}
			else if (HLSManifestParser.PREF_BITRATE != -1)
			{
				//If there is a preferred bitrate set by kaltura, tests all streams to find highest bitrate below the preferred
				preferredIndex = 0;
				var preferredDistance:Number = Number.MAX_VALUE;

				for(var k:int=0; k<items.length; k++)
				{
					var curItem:DynamicStreamingItem = items[k];
					var curDist:Number = items[k].bitrate - HLSManifestParser.PREF_BITRATE;

					/// Reject too low or not improved items.
					if(curDist < 0 || curDist >= preferredDistance)
						continue;

					preferredIndex = k;
					preferredDistance = curDist;
				}
			}
			else
			{
				//Sets the preferred index to the middle (or higher of the 2 middle) bitrate streams
				preferredIndex = items.length / 2;
			}
			stream.initialIndex = preferredIndex;

			stream.addMetadataValue( HLSMetadataNamespaces.PLAYABLE_RESOURCE_METADATA, true );

			var loadedElem:MediaElement = new HLSVideoElement( stream, new M2TSNetLoader() );
			LoadFromDocumentLoadTrait( loadTrait ).mediaElement = loadedElem;

			if ( parser.subtitlePlayLists.length > 0 )
			{
				var subtitleTrait:SubtitleTrait = new SubtitleTrait();
				subtitleTrait.playLists = parser.subtitlePlayLists;
				subtitleTrait.owningMediaElement = loadedElem;
				stream.subtitleTrait = subtitleTrait;
			}

			if ( isDVR )
			{
				var dvrMetadata:Metadata = new Metadata();
				stream.addMetadataValue(MetadataNamespaces.DVR_METADATA, dvrMetadata);
			}

			updateLoadTrait( loadTrait, LoadState.READY );
		}

		private function onError(event:ErrorEvent):void
		{
			manifestLoader.removeEventListener(Event.COMPLETE, onComplete);
			manifestLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			manifestLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

			updateLoadTrait(loadTrait, LoadState.LOAD_ERROR);
			loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(0, event.text)));
		}

		override public function canHandleResource(resource:MediaResourceBase):Boolean
		{
			var supported:int = MediaTypeUtil.checkMetadataMatchWithResource(resource, new Vector.<String>(), supportedMimeTypes);

			if ( supported == MediaTypeUtil.METADATA_MATCH_FOUND )
				return true;

			if (!(resource is URLResource))
				return false;

			var url:URL = new URL((resource as URLResource).url);
			if (url.extension != "m3u8" && url.extension != "m3u")
				return false;

			return true;
		}

	}
}

package com.kaltura.hls
{
	import com.kaltura.hls.m2ts.M2TSNetLoader;
	import com.kaltura.hls.manifest.HLSManifestParser;
	import com.kaltura.hls.manifest.HLSManifestPlaylist;
	import com.kaltura.hls.manifest.HLSManifestStream;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;

	import org.osmf.elements.VideoElement;
	import org.osmf.elements.proxyClasses.LoadFromDocumentLoadTrait;
	import org.osmf.events.MediaError;
	import org.osmf.events.MediaErrorEvent;
	import org.osmf.media.DefaultMediaFactory;
	import org.osmf.media.MediaElement;
	import org.osmf.media.MediaResourceBase;
	import org.osmf.media.MediaTypeUtil;
	import org.osmf.media.URLResource;
	import org.osmf.metadata.Metadata;
	import org.osmf.metadata.MetadataNamespaces;
	import org.osmf.net.DynamicStreamingItem;
	import org.osmf.net.StreamType;
	import org.osmf.net.StreamingItem;
	import org.osmf.net.StreamingItemType;
	import org.osmf.traits.LoadState;
	import org.osmf.traits.LoadTrait;
	import org.osmf.traits.LoaderBase;
	import org.osmf.utils.URL;

	public class HLSLoader extends LoaderBase
	{
		protected var loadTrait:LoadTrait;
		protected var manifestLoader:URLLoader;
		protected var parser:HLSManifestParser;
		protected var factory:DefaultMediaFactory = new DefaultMediaFactory();
		private var supportedMimeTypes:Vector.<String> = new Vector.<String>();

		public function HLSLoader()
		{
			super();

			supportedMimeTypes.push( "application/x-mpegURL" );
			supportedMimeTypes.push( "application/vnd.apple.mpegURL" );
			supportedMimeTypes.push( "vnd.apple.mpegURL" );
			supportedMimeTypes.push( "video/MP2T" );
		}

		override protected function executeLoad(loadTrait:LoadTrait):void
		{
			this.loadTrait = loadTrait;

			updateLoadTrait(loadTrait, LoadState.LOADING);

			var url:String = URLResource(loadTrait.resource).url;
			manifestLoader = new URLLoader(new URLRequest(url));
			manifestLoader.addEventListener(Event.COMPLETE, onComplete);
			manifestLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			manifestLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
		}

		private function onComplete(event:Event):void
		{
			manifestLoader.removeEventListener(Event.COMPLETE, onComplete);
			manifestLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			manifestLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

			try {
				var resourceData:String = String((event.target as URLLoader).data);

				var url:String = URLResource(loadTrait.resource).url;

				// Start parsing the manifest.
				parser = new HLSManifestParser();
				parser.addEventListener(Event.COMPLETE, onManifestComplete);
				parser.parse(resourceData, url);
			}
			catch (parseError:Error)
			{
				updateLoadTrait(loadTrait, LoadState.LOAD_ERROR);
				loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(parseError.errorID, parseError.message)));
			}
		}

		protected function onManifestComplete(event:Event):void
		{
			trace("Manifest is loaded.");

			var isDVR:Boolean = false;

			// Construct the streaming resource and set it as our resource.
			var stream:HLSStreamingResource = new HLSStreamingResource(URLResource(loadTrait.resource).url, "", StreamType.DVR);
			stream.manifest = parser;

			var item:DynamicStreamingItem;
			var items:Vector.<DynamicStreamingItem> = new Vector.<DynamicStreamingItem>();
			for(var i:int=0; i<parser.streams.length; i++)
			{
				var curStream:HLSManifestStream = parser.streams[i];

				//If there is more than one streaming quality available, check to see if filtering is required
				if (parser.streams.length > 1)
				{
					//Checks to see if there are min/max bitrate restrictions then throws out the outliers
					if(HLSManifestParser.MAX_BITRATE != -1 && curStream.bandwidth > HLSManifestParser.MAX_BITRATE ||
						HLSManifestParser.MIN_BITRATE != -1 && curStream.bandwidth < HLSManifestParser.MIN_BITRATE)
					{
						continue;
					}
				}

				item = new DynamicStreamingItem(curStream.uri, curStream.bandwidth, curStream.width, curStream.height);
				curStream.dynamicStream = item;
				items.push(item);
				if ( !(curStream.manifest ? curStream.manifest.streamEnds : false) ) isDVR = true;

				// Create dynamic streaming items for the backup streams
				if (!curStream.backupStream)
					continue;

				var mainStream:HLSManifestStream = curStream;
				curStream = curStream.backupStream;
				while (curStream != mainStream)
				{
					curStream.dynamicStream = new DynamicStreamingItem(curStream.uri, curStream.bandwidth, curStream.width, curStream.height);
					curStream = curStream.backupStream;
				}
			}

			// Deal with single rate M3Us by stuffing a single stream in.
			if(items.length == 0)
			{
				item = new DynamicStreamingItem(URLResource(loadTrait.resource).url, 0, 0, 0);
				items.push(item);

				// Also set the DVR state
				if ( !stream.manifest.streamEnds ) isDVR = true;
			}

			var alternateAudioItems:Vector.<StreamingItem> = new <StreamingItem>[];
			for ( var j:int = 0; j < parser.playLists.length; j++ )
			{
				var playList:HLSManifestPlaylist = parser.playLists[ j ];
				if ( !playList.manifest ) continue;
				var audioItem:StreamingItem = new StreamingItem( StreamingItemType.AUDIO, playList.name, 0, playList );
				alternateAudioItems.push( audioItem );
			}

			stream.streamItems = items;
			stream.alternativeAudioStreamItems = alternateAudioItems;

			var preferredIndex:int;

			//If there is only one stream quality (or less) sets default to first stream
			if (items.length <= 1)
			{
				preferredIndex = 0;
			}
			else if (HLSManifestParser.PREF_BITRATE != -1)
			{
				//If there is a preferred bitrate set by kaltura, tests all streams to find highest bitrate below the preferred
				preferredIndex = 0;
				var preferredDistance:Number = Number.MAX_VALUE;

				for(var k:int=0; k<items.length; k++)
				{
					var curItem:DynamicStreamingItem = items[k];
					var curDist:Number = Math.abs(items[k].bitrate - HLSManifestParser.PREF_BITRATE);

					/// Reject too low or not improved items.
					if(curDist < 0 || curDist >= preferredDistance)
						continue;

					preferredIndex = k;
					preferredDistance = curDist;
				}
			}
			else
			{
				//Sets the preferred index to the middle (or higher of the 2 middle) bitrate streams
				preferredIndex = items.length / 2;
			}
			stream.initialIndex = preferredIndex;

			stream.addMetadataValue( HLSMetadataNamespaces.PLAYABLE_RESOURCE_METADATA, true );

			var loadedElem:MediaElement = new HLSVideoElement( stream, new M2TSNetLoader() );
			LoadFromDocumentLoadTrait( loadTrait ).mediaElement = loadedElem;

			if ( parser.subtitlePlayLists.length > 0 )
			{
				var subtitleTrait:SubtitleTrait = new SubtitleTrait();
				subtitleTrait.playLists = parser.subtitlePlayLists;
				subtitleTrait.owningMediaElement = loadedElem;
				stream.subtitleTrait = subtitleTrait;
			}

			if ( isDVR )
			{
				var dvrMetadata:Metadata = new Metadata();
				stream.addMetadataValue(MetadataNamespaces.DVR_METADATA, dvrMetadata);
			}

			updateLoadTrait( loadTrait, LoadState.READY );
		}

		private function onError(event:ErrorEvent):void
		{
			manifestLoader.removeEventListener(Event.COMPLETE, onComplete);
			manifestLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			manifestLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);

			updateLoadTrait(loadTrait, LoadState.LOAD_ERROR);
			loadTrait.dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(0, event.text)));
		}

		override public function canHandleResource(resource:MediaResourceBase):Boolean
		{
			var supported:int = MediaTypeUtil.checkMetadataMatchWithResource(resource, new Vector.<String>(), supportedMimeTypes);

			if ( supported == MediaTypeUtil.METADATA_MATCH_FOUND )
				return true;

			if (!(resource is URLResource))
				return false;

			var url:URL = new URL((resource as URLResource).url);
			if (url.extension != "m3u8" && url.extension != "m3u")
				return false;

			return true;
		}

	}
}

Correct preferred bitrate selection logic to use abs.


/*
 * Copyright (c) 2012 the original author or authors
 *
 * Permission is hereby granted to use, modify, and distribute this file
 * in accordance with the terms of the license agreement accompanying it.
 */

package org.swiftsuspenders.typedescriptions
{
	import flash.utils.Dictionary;

	import org.swiftsuspenders.Injector;

	public class ConstructorInjectionPoint extends MethodInjectionPoint
	{
		//----------------------               Public Methods               ----------------------//
		public function ConstructorInjectionPoint(parameters : Array, requiredParameters : uint,
			injectParameters : Dictionary)
		{
			super('ctor', parameters, requiredParameters, false, injectParameters);
		}

		public function createInstance(type : Class, injector : Injector) : Object
		{
			var p : Array = gatherParameterValues(type, type, injector);
			var result : Object;
			//the only way to implement ctor injections, really!
			switch (p.length)
			{
				case 1 : result = new type(p[0]); break;
				case 2 : result = new type(p[0], p[1]); break;
				case 3 : result = new type(p[0], p[1], p[2]); break;
				case 4 : result = new type(p[0], p[1], p[2], p[3]); break;
				case 5 : result = new type(p[0], p[1], p[2], p[3], p[4]); break;
				case 6 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5]); break;
				case 7 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); break;
				case 8 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); break;
				case 9 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); break;
				case 10 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); break;
			}
			p.length = 0;
			return result;
		}
	}
}

/*
 * Copyright (c) 2012 the original author or authors
 *
 * Permission is hereby granted to use, modify, and distribute this file
 * in accordance with the terms of the license agreement accompanying it.
 */

package org.swiftsuspenders.typedescriptions
{
	import flash.utils.Dictionary;

	import org.swiftsuspenders.Injector;

	public class ConstructorInjectionPoint extends MethodInjectionPoint
	{
		//----------------------               Public Methods               ----------------------//
		public function ConstructorInjectionPoint(parameters : Array, requiredParameters : uint,
			injectParameters : Dictionary)
		{
			super('ctor', parameters, requiredParameters, false, injectParameters);
		}

		public function createInstance(type : Class, injector : Injector) : Object
		{
			var p : Array = gatherParameterValues(type, type, injector);
			var result : Object;
			//the only way to implement ctor injections, really!
			switch (p.length)
			{
				case 1 : result = new type(p[0]); break;
				case 2 : result = new type(p[0], p[1]); break;
				case 3 : result = new type(p[0], p[1], p[2]); break;
				case 4 : result = new type(p[0], p[1], p[2], p[3]); break;
				case 5 : result = new type(p[0], p[1], p[2], p[3], p[4]); break;
				case 6 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5]); break;
				case 7 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); break;
				case 8 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); break;
				case 9 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); break;
				case 10 : result = new type(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); break;
				default: throw new Error("The constructor for " + type + " has too many arguments, maximum is 10");
			}
			p.length = 0;
			return result;
		}
	}
}

Throw an exception if the class couldn't be instantiated.


// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2011  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
package com.kaltura.delegates {

	import com.kaltura.KalturaClient;
	import com.kaltura.config.IKalturaConfig;
	import com.kaltura.config.KalturaConfig;
	import com.kaltura.core.KClassFactory;
	import com.kaltura.encryption.MD5;
	import com.kaltura.errors.KalturaError;
	import com.kaltura.events.KalturaEvent;
	import com.kaltura.net.KalturaCall;
	import com.kaltura.types.KalturaResponseType;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.FileReference;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.Timer;
	import flash.utils.getDefinitionByName;

	public class WebDelegateBase extends EventDispatcher implements IKalturaCallDelegate {

		public static var CONNECT_TIME:int = 60000; //60 secs
		public static var LOAD_TIME:int = 120000; //120 secs

		protected var connectTimer:Timer;
		protected var loadTimer:Timer;

		protected var _call:KalturaCall;
		protected var _config:KalturaConfig;

		protected var loader:URLLoader;
		protected var fileRef:FileReference;

		//Setters & getters
		public function get call():KalturaCall {
			return _call;
		}

		public function set call(newVal:KalturaCall):void {
			_call = newVal;
		}

		public function get config():IKalturaConfig {
			return _config;
		}

		public function set config(newVal:IKalturaConfig):void {
			_config = newVal as KalturaConfig;
		}

		public function WebDelegateBase(call:KalturaCall = null, config:KalturaConfig = null) {
			this.call = call;
			this.config = config;
			if (!call)
				return; //maybe a multi request
			if (call.useTimeout) {
				connectTimer = new Timer(CONNECT_TIME, 1);
				connectTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onConnectTimeout);

				loadTimer = new Timer(LOAD_TIME, 1);
				loadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onLoadTimeOut);
			}
			execute();
		}

		public function close():void {
			try {
				loader.close();
			}
			catch (e:*) {
			}

			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}
		}

		protected function onConnectTimeout(event:TimerEvent):void {
			var kError:KalturaError = new KalturaError();
			kError.errorCode = "CONNECTION_TIMEOUT";
			kError.errorMsg = "Connection Timeout: " + CONNECT_TIME / 1000 + " sec with no post command from kaltura client.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));

			loadTimer.stop();
			close();
		}

		protected function onLoadTimeOut(event:TimerEvent):void {
			connectTimer.stop();

			close();

			var kError:KalturaError = new KalturaError();
			kError.errorCode = "POST_TIMEOUT";
			kError.errorMsg = "Post Timeout: " + LOAD_TIME / 1000 + " sec with no post result.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		protected function execute():void {
			if (call == null) {
				throw new Error('No call defined.');
			}
			post(); //post the call
		}

		/**
		* Helper function for sending the call straight to the server
		*/
		protected function post():void {

			addOptionalArguments();

			formatRequest();

			sendRequest();

			if (call.useTimeout) {
				connectTimer.start();
			}
		}

		protected function formatRequest():void {
			//The configuration is stronger then the args
			if (_config.partnerId != null && _call.args["partnerId"] == -1)
				_call.setRequestArgument("partnerId", _config.partnerId);

			if (_config.ks != null)
				_call.setRequestArgument("ks", _config.ks);

			if (_config.clientTag != null)
				_call.setRequestArgument("clientTag", _config.clientTag);

			_call.setRequestArgument("ignoreNull", _config.ignoreNull);
			_call.setRequestArgument("apiVersion", KalturaClient.API_VERSION);
			_call.setRequestArgument("format", KalturaResponseType.RESPONSE_TYPE_XML);

			//Create signature hash.
			//call.setRequestArgument("kalsig", getMD5Checksum(call));
		}

		protected function getMD5Checksum(call:KalturaCall):String {
			var props:Array = new Array();
			for (var arg:String in call.args)
				props.push(arg);

			props.sort();

			var s:String = "";
			for each (var prop:String in props) {
				if ( call.args[prop] )
					s += prop +  call.args[prop];
			}

			return MD5.encrypt(s);
		}

		protected function sendRequest():void {

			//construct the loader
			createURLLoader();

			//Create signature hash.
			var kalsig:String = getMD5Checksum(call);

			//create the service request for normal calls
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action + "&kalsig=" + kalsig;;

			if (_call.method == URLRequestMethod.GET)
				url += "&";

			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
			req.data = call.args;

			loader.dataFormat = URLLoaderDataFormat.TEXT;
			loader.load(req);
		}

		protected function createURLLoader():void {
			loader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onDataComplete);
			loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
			loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.addEventListener(Event.OPEN, onOpen);
		}

		protected function onHTTPStatus(event:HTTPStatusEvent):void {
		}

		protected function onOpen(event:Event):void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.start();
			}
		}

		protected function addOptionalArguments():void {
			//add optional args here
		}

		// Event Handlers

		/**
		 * try to process received data.
		 * if procesing failed, let call handle the processing error
		 * @param event load complete event
		 */
		protected function onDataComplete(event:Event):void {
			try {
				handleResult(XML(event.target.data));
			}
			catch (e:Error) {
				var kErr:KalturaError = new KalturaError();
				kErr.errorCode = String(e.errorID);
				kErr.errorMsg = e.message;
				_call.handleError(kErr);
			}
		}

		/**
		 * handle io or security error events from the loader.
		 * create relevant KalturaError, let the call process it.
		 * @param event
		 */
		protected function onError(event:ErrorEvent):void {
			clean();
			var kError:KalturaError = createKalturaError(event, loader.data);

			if (!kError) {
				kError = new KalturaError();
				kError.errorMsg = event.text;
				//kError.errorCode;
			}

			call.handleError(kError);

			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		/**
		 * parse the server's response and let the call process it.
		 * @param result  server's response
		 */
		protected function handleResult(result:XML):void {
			clean();

			var error:KalturaError = validateKalturaResponse(result);

			if (error == null) {
				var digestedResult:Object = parse(result);
				call.handleResult(digestedResult);
			}
			else {
				call.handleError(error);
			}
		}

		/**
		* stop timers and clean event listeners
		 */
		protected function clean():void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}

			if (loader == null) {
				return;
			}

			loader.removeEventListener(Event.COMPLETE, onDataComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.removeEventListener(Event.OPEN, onOpen);
		}

		/**
		* create the correct object and populate it with the given values. if the needed class is not found
		 * in the file, a generic object is created with attributes matching the XML attributes.
		* Override this parssing function in the specific delegate to create the correct object.
		* @param   result      instance attributes
		* @return an instance of the class declared by the given XML.
		* */
		public function parse(result:XML):* {
			//by defualt create the response object
			var cls:Class;
			try {
				cls = getDefinitionByName('com.kaltura.vo.' + result.result.objectType) as Class;
			}
			catch (e:Error) {
				cls = Object;
			}
			var obj:* = (new KClassFactory(cls)).newInstanceFromXML(result.result);
			return obj;
		}

		/**
		 * If the result string holds an error, return a KalturaError object with
		 * relevant values. <br/>
		 * Overide this to create validation object and fill it.
		 * @param result  the string returned from the server.
		 * @return  matching error object
		 */
		protected function validateKalturaResponse(result:String):KalturaError {
			var kError:KalturaError = null;
			var xml:XML = XML(result);
			if (xml.result.hasOwnProperty('error')
				&& xml.result.error.hasOwnProperty('code')
				&& xml.result.error.hasOwnProperty('message')) {

				kError = new KalturaError();
				kError.errorCode = String(xml.result.error.code);
				kError.errorMsg = xml.result.error.message;
				dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
			}

			return kError;
		}

		/**
		 * create error object and fill it with relevant details
		 * @param event
		 * @param loaderData
		 * @return detailed KalturaError to be processed
		 */
		protected function createKalturaError(event:ErrorEvent, loaderData:*):KalturaError {
			var ke:KalturaError = new KalturaError();
			ke.errorMsg = event.text;
			return ke;
		}

		/**
		* create the url that is used for serve actions
		* @param call    the KalturaCall that defines the required parameters
		 * @return URLRequest with relevant parameters
		* */
		public function getServeUrl(call:KalturaCall):URLRequest {
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action;
			for (var key:String in call.args) {
				url += "&" + key + "=" + call.args[key];
			}
			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
//          req.data = call.args;
			return req;
		}

	}
}

// ===================================================================================================
//                           _  __     _ _
//                          | |/ /__ _| | |_ _  _ _ _ __ _
//                          | ' </ _` | |  _| || | '_/ _` |
//                          |_|\_\__,_|_|\__|\_,_|_| \__,_|
//
// This file is part of the Kaltura Collaborative Media Suite which allows users
// to do with audio, video, and animation what Wiki platfroms allow them to do with
// text.
//
// Copyright (C) 2006-2011  Kaltura Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// @ignore
// ===================================================================================================
package com.kaltura.delegates {

	import com.kaltura.KalturaClient;
	import com.kaltura.config.IKalturaConfig;
	import com.kaltura.config.KalturaConfig;
	import com.kaltura.core.KClassFactory;
	import com.kaltura.encryption.MD5;
	import com.kaltura.errors.KalturaError;
	import com.kaltura.events.KalturaEvent;
	import com.kaltura.net.KalturaCall;
	import com.kaltura.types.KalturaResponseType;

	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.TimerEvent;
	import flash.net.FileReference;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.utils.Timer;
	import flash.utils.getDefinitionByName;

	public class WebDelegateBase extends EventDispatcher implements IKalturaCallDelegate {

		public static var CONNECT_TIME:int = 60000; //60 secs
		public static var LOAD_TIME:int = 120000; //120 secs

		protected var connectTimer:Timer;
		protected var loadTimer:Timer;

		protected var _call:KalturaCall;
		protected var _config:KalturaConfig;

		protected var loader:URLLoader;
		protected var fileRef:FileReference;

		//Setters & getters
		public function get call():KalturaCall {
			return _call;
		}

		public function set call(newVal:KalturaCall):void {
			_call = newVal;
		}

		public function get config():IKalturaConfig {
			return _config;
		}

		public function set config(newVal:IKalturaConfig):void {
			_config = newVal as KalturaConfig;
		}

		public function WebDelegateBase(call:KalturaCall = null, config:KalturaConfig = null) {
			this.call = call;
			this.config = config;
			if (!call)
				return; //maybe a multi request
			if (call.useTimeout) {
				connectTimer = new Timer(CONNECT_TIME, 1);
				connectTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onConnectTimeout);

				loadTimer = new Timer(LOAD_TIME, 1);
				loadTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onLoadTimeOut);
			}
			execute();
		}

		public function close():void {
			try {
				loader.close();
			}
			catch (e:*) {
			}

			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}
		}

		protected function onConnectTimeout(event:TimerEvent):void {
			var kError:KalturaError = new KalturaError();
			kError.errorCode = "CONNECTION_TIMEOUT";
			kError.errorMsg = "Connection Timeout: " + CONNECT_TIME / 1000 + " sec with no post command from kaltura client.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));

			loadTimer.stop();
			close();
		}

		protected function onLoadTimeOut(event:TimerEvent):void {
			connectTimer.stop();

			close();

			var kError:KalturaError = new KalturaError();
			kError.errorCode = "POST_TIMEOUT";
			kError.errorMsg = "Post Timeout: " + LOAD_TIME / 1000 + " sec with no post result.";
			_call.handleError(kError);
			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		protected function execute():void {
			if (call == null) {
				throw new Error('No call defined.');
			}
			post(); //post the call
		}

		/**
		* Helper function for sending the call straight to the server
		*/
		protected function post():void {

			addOptionalArguments();

			formatRequest();

			sendRequest();

			if (call.useTimeout) {
				connectTimer.start();
			}
		}

		protected function formatRequest():void {
			//The configuration is stronger then the args
			if (_config.partnerId != null && _call.args["partnerId"] == -1)
				_call.setRequestArgument("partnerId", _config.partnerId);

			if (_config.ks != null)
				_call.setRequestArgument("ks", _config.ks);

			if (_config.clientTag != null)
				_call.setRequestArgument("clientTag", _config.clientTag);

			_call.setRequestArgument("ignoreNull", _config.ignoreNull);
			_call.setRequestArgument("apiVersion", KalturaClient.API_VERSION);
			//_call.setRequestArgument("format", KalturaResponseType.RESPONSE_TYPE_XML);

			//Create signature hash.
			//call.setRequestArgument("kalsig", getMD5Checksum(call));
		}

		protected function getMD5Checksum(call:KalturaCall):String {
			var props:Array = new Array();
			for (var arg:String in call.args)
				props.push(arg);

			props.sort();

			var s:String = "";
			for each (var prop:String in props) {
				if ( call.args[prop] )
					s += prop +  call.args[prop];
			}

			return MD5.encrypt(s);
		}

		protected function sendRequest():void {

			//construct the loader
			createURLLoader();

			//Create signature hash.
			var kalsig:String = getMD5Checksum(call);

			//create the service request for normal calls
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action + "&kalsig=" + kalsig;;

			if (_call.method == URLRequestMethod.GET)
				url += "&";

			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
			req.data = call.args;

			loader.dataFormat = URLLoaderDataFormat.TEXT;
			loader.load(req);
		}

		protected function createURLLoader():void {
			loader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onDataComplete);
			loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onHTTPStatus);
			loader.addEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.addEventListener(Event.OPEN, onOpen);
		}

		protected function onHTTPStatus(event:HTTPStatusEvent):void {
		}

		protected function onOpen(event:Event):void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.start();
			}
		}

		protected function addOptionalArguments():void {
			//add optional args here
		}

		// Event Handlers

		/**
		 * try to process received data.
		 * if procesing failed, let call handle the processing error
		 * @param event load complete event
		 */
		protected function onDataComplete(event:Event):void {
			try {
				handleResult(XML(event.target.data));
			}
			catch (e:Error) {
				var kErr:KalturaError = new KalturaError();
				kErr.errorCode = String(e.errorID);
				kErr.errorMsg = e.message;
				_call.handleError(kErr);
			}
		}

		/**
		 * handle io or security error events from the loader.
		 * create relevant KalturaError, let the call process it.
		 * @param event
		 */
		protected function onError(event:ErrorEvent):void {
			clean();
			var kError:KalturaError = createKalturaError(event, loader.data);

			if (!kError) {
				kError = new KalturaError();
				kError.errorMsg = event.text;
				//kError.errorCode;
			}

			call.handleError(kError);

			dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
		}

		/**
		 * parse the server's response and let the call process it.
		 * @param result  server's response
		 */
		protected function handleResult(result:XML):void {
			clean();

			var error:KalturaError = validateKalturaResponse(result);

			if (error == null) {
				var digestedResult:Object = parse(result);
				call.handleResult(digestedResult);
			}
			else {
				call.handleError(error);
			}
		}

		/**
		* stop timers and clean event listeners
		 */
		protected function clean():void {
			if (call.useTimeout) {
				connectTimer.stop();
				loadTimer.stop();
			}

			if (loader == null) {
				return;
			}

			loader.removeEventListener(Event.COMPLETE, onDataComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
			loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			loader.removeEventListener(Event.OPEN, onOpen);
		}

		/**
		* create the correct object and populate it with the given values. if the needed class is not found
		 * in the file, a generic object is created with attributes matching the XML attributes.
		* Override this parssing function in the specific delegate to create the correct object.
		* @param   result      instance attributes
		* @return an instance of the class declared by the given XML.
		* */
		public function parse(result:XML):* {
			//by defualt create the response object
			var cls:Class;
			try {
				cls = getDefinitionByName('com.kaltura.vo.' + result.result.objectType) as Class;
			}
			catch (e:Error) {
				cls = Object;
			}
			var obj:* = (new KClassFactory(cls)).newInstanceFromXML(result.result);
			return obj;
		}

		/**
		 * If the result string holds an error, return a KalturaError object with
		 * relevant values. <br/>
		 * Overide this to create validation object and fill it.
		 * @param result  the string returned from the server.
		 * @return  matching error object
		 */
		protected function validateKalturaResponse(result:String):KalturaError {
			var kError:KalturaError = null;
			var xml:XML = XML(result);
			if (xml.result.hasOwnProperty('error')
				&& xml.result.error.hasOwnProperty('code')
				&& xml.result.error.hasOwnProperty('message')) {

				kError = new KalturaError();
				kError.errorCode = String(xml.result.error.code);
				kError.errorMsg = xml.result.error.message;
				dispatchEvent(new KalturaEvent(KalturaEvent.FAILED, false, false, false, null, kError));
			}

			return kError;
		}

		/**
		 * create error object and fill it with relevant details
		 * @param event
		 * @param loaderData
		 * @return detailed KalturaError to be processed
		 */
		protected function createKalturaError(event:ErrorEvent, loaderData:*):KalturaError {
			var ke:KalturaError = new KalturaError();
			ke.errorMsg = event.text;
			return ke;
		}

		/**
		* create the url that is used for serve actions
		* @param call    the KalturaCall that defines the required parameters
		 * @return URLRequest with relevant parameters
		* */
		public function getServeUrl(call:KalturaCall):URLRequest {
			var url:String = _config.protocol + _config.domain + "/" + _config.srvUrl + "?service=" + call.service + "&action=" + call.action;
			for (var key:String in call.args) {
				url += "&" + key + "=" + call.args[key];
			}
			var req:URLRequest = new URLRequest(url);
			req.contentType = "application/x-www-form-urlencoded";
			req.method = call.method;
//          req.data = call.args;
			return req;
		}

	}
}

remove "format" from kalsig calculation

remove "format" from kalsig calculation


package
{
	import com.kaltura.kdpfl.model.LayoutProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.plugin.IPlugin;
	import com.kaltura.kdpfl.plugin.KPluginEvent;
	import com.kaltura.kdpfl.plugin.WVPluginInfo;

	import flash.display.Sprite;

	import org.osmf.events.MediaFactoryEvent;
	import org.osmf.media.DefaultMediaFactory;
	import org.osmf.media.PluginInfoResource;
	import org.puremvc.as3.interfaces.IFacade;

	public dynamic class widevinePluginCode extends Sprite implements IPlugin
	{
		protected var _localMediaFactory : DefaultMediaFactory;

		//Alerts texts, can be overriden from uiconf XML or flashvars.
		public var alert_title:String	= "Error";
		public var warning_title:String	= "Warning";
		public var alert_emm_falied:String = "We're sorry, you don’t have a license to play this video.";
		public var alert_emm_error:String = "We're sorry, we failed to obtain a license for playing this video.";
		public var alert_emm_expired:String = "We're sorry, the license to play this video has expired.";
		public var alert_log_error:String = "Message Logging Error (error code: {0})";
		public var alert_dcp_stop:String = "Playback is stopping due to detection of an illegal content copying attempt.";
		public var alert_dcp_alert:String = "An illegal attempt for content copying was detected.";
		public var alert_missing_plugin:String = "Widevine Video Optimizer plugin is needed for enabling video playback in this player.";

		public function widevinePluginCode()
		{
			trace('Widevine plugin v3');
			super();
		}

		public function initializePlugin(facade:IFacade):void
		{
			_localMediaFactory = (facade.retrieveProxy(MediaProxy.NAME) as MediaProxy).vo.mediaFactory;
			var wvPluginInfo:WVPluginInfo = new WVPluginInfo();

			var lp:LayoutProxy = (facade.retrieveProxy(LayoutProxy.NAME) as LayoutProxy);
			for each( var i:Object in lp.components)
			{
				if(i.hasOwnProperty("className") && i.className == "KMediaPlayer" )
				{
					trace(i.ui.width,i.ui.height );
					WVPluginInfo.mediaWidth = i.ui.width;
					WVPluginInfo.mediaHeight = i.ui.height;
					break;
				}
			}

			_localMediaFactory.addEventListener(MediaFactoryEvent.PLUGIN_LOAD, onOSMFPluginLoaded);
			_localMediaFactory.addEventListener(MediaFactoryEvent.PLUGIN_LOAD_ERROR, onOSMFPluginLoadError);
			_localMediaFactory.loadPlugin(new PluginInfoResource(wvPluginInfo) );

			var wvm:WvMediator = new WvMediator(this, wvPluginInfo);
			facade.registerMediator(wvm);
		}
		/**
		 * Listener for the LOAD_COMPLETE event.
		 * @param e - MediaFactoryEvent
		 *
		 */
		protected function onOSMFPluginLoaded (e : MediaFactoryEvent) : void
		{
			_localMediaFactory.removeEventListener(MediaFactoryEvent.PLUGIN_LOAD, onOSMFPluginLoaded);
			dispatchEvent( new KPluginEvent (KPluginEvent.KPLUGIN_INIT_COMPLETE) );
		}
		/**
		 * Listener for the LOAD_ERROR event.
		 * @param e - MediaFactoryEvent
		 *
		 */
		protected function onOSMFPluginLoadError (e : MediaFactoryEvent) : void
		{
			_localMediaFactory.removeEventListener(MediaFactoryEvent.PLUGIN_LOAD_ERROR, onOSMFPluginLoadError);
			dispatchEvent( new KPluginEvent (KPluginEvent.KPLUGIN_INIT_FAILED) );
		}
		public function setSkin(styleName:String, setSkinSize:Boolean=false):void
		{
		}

	}
}

package
{
	import com.kaltura.kdpfl.model.ConfigProxy;
	import com.kaltura.kdpfl.model.LayoutProxy;
	import com.kaltura.kdpfl.model.MediaProxy;
	import com.kaltura.kdpfl.plugin.IPlugin;
	import com.kaltura.kdpfl.plugin.KPluginEvent;
	import com.kaltura.kdpfl.plugin.WVPluginInfo;

	import flash.display.Sprite;

	import org.osmf.events.MediaFactoryEvent;
	import org.osmf.media.DefaultMediaFactory;
	import org.osmf.media.PluginInfoResource;
	import org.puremvc.as3.interfaces.IFacade;

	public dynamic class widevinePluginCode extends Sprite implements IPlugin
	{
		protected var _localMediaFactory : DefaultMediaFactory;

		//Alerts texts, can be overriden from uiconf XML or flashvars.
		public var alert_title:String	= "Error";
		public var warning_title:String	= "Warning";
		public var alert_emm_falied:String = "We're sorry, you don’t have a valid license for this video.";
		public var alert_emm_error:String = "We're sorry, a license request for this video failed.";
		public var alert_emm_expired:String = "We're sorry, you don’t have a valid license for this video.";
		public var alert_log_error:String = "Message logging failed (error code: {0})";
		public var alert_dcp_stop:String = "Playback stopped due to the detection of copying attempt.";
		public var alert_dcp_alert:String = "A copying attempt was detected.";
		public var alert_missing_plugin:String = "Widevine Video Optimizer plugin is needed for enabling video playback in this player.";

		public var flavorTags:String;

		public function widevinePluginCode()
		{
			trace('Widevine plugin v3');
			super();
		}

		public function initializePlugin(facade:IFacade):void
		{
			_localMediaFactory = (facade.retrieveProxy(MediaProxy.NAME) as MediaProxy).vo.mediaFactory;
			var wvPluginInfo:WVPluginInfo = new WVPluginInfo();

			var lp:LayoutProxy = (facade.retrieveProxy(LayoutProxy.NAME) as LayoutProxy);
			for each( var i:Object in lp.components)
			{
				if(i.hasOwnProperty("className") && i.className == "KMediaPlayer" )
				{
					trace(i.ui.width,i.ui.height );
					WVPluginInfo.mediaWidth = i.ui.width;
					WVPluginInfo.mediaHeight = i.ui.height;
					break;
				}
			}

			_localMediaFactory.addEventListener(MediaFactoryEvent.PLUGIN_LOAD, onOSMFPluginLoaded);
			_localMediaFactory.addEventListener(MediaFactoryEvent.PLUGIN_LOAD_ERROR, onOSMFPluginLoadError);
			_localMediaFactory.loadPlugin(new PluginInfoResource(wvPluginInfo) );

			var wvm:WvMediator = new WvMediator(this, wvPluginInfo);
			facade.registerMediator(wvm);

			if (flavorTags)
			{
				(facade.retrieveProxy(ConfigProxy.NAME) as ConfigProxy).vo.flashvars.flavorTags = flavorTags;
			}
		}
		/**
		 * Listener for the LOAD_COMPLETE event.
		 * @param e - MediaFactoryEvent
		 *
		 */
		protected function onOSMFPluginLoaded (e : MediaFactoryEvent) : void
		{
			_localMediaFactory.removeEventListener(MediaFactoryEvent.PLUGIN_LOAD, onOSMFPluginLoaded);
			dispatchEvent( new KPluginEvent (KPluginEvent.KPLUGIN_INIT_COMPLETE) );
		}
		/**
		 * Listener for the LOAD_ERROR event.
		 * @param e - MediaFactoryEvent
		 *
		 */
		protected function onOSMFPluginLoadError (e : MediaFactoryEvent) : void
		{
			_localMediaFactory.removeEventListener(MediaFactoryEvent.PLUGIN_LOAD_ERROR, onOSMFPluginLoadError);
			dispatchEvent( new KPluginEvent (KPluginEvent.KPLUGIN_INIT_FAILED) );
		}
		public function setSkin(styleName:String, setSkinSize:Boolean=false):void
		{
		}

	}
}

fix tests, support flavor tags

widevine: fix tests, support flavor tags


// Editor debug icons
// to add new std debug icons just add IconType, IconsTypesMaterials and ComponentTypes items

enum IconsTypes
{
    ICON_POINT_LIGHT = 0,
    ICON_SPOT_LIGHT,
    ICON_DIRECTIONAL_LIGHT,
    ICON_CAMERA,
    ICON_SOUND_SOURCE,
    ICON_SOUND_SOURCE_3D,
    ICON_SOUND_LISTENERS,
    ICON_ZONE,
    ICON_SPLINE_PATH,
    ICON_TRIGGER,
    ICON_CUSTOM_GEOMETRY,
    ICON_PARTICLE_EMITTER,
    ICON_COUNT
}

enum IconsColorType
{
    ICON_COLOR_DEFAULT = 0,
    ICON_COLOR_SPLINE_PATH_BEGIN,
    ICON_COLOR_SPLINE_PATH_END
}

Array<Color> debugIconsColors = { Color(1,1,1) , Color(1,1,0), Color(0,1,0) };

Array<String> IconsTypesMaterials = {"DebugIconPointLight.xml",
                                     "DebugIconSpotLight.xml",
                                     "DebugIconLight.xml",
                                     "DebugIconCamera.xml",
                                     "DebugIconSoundSource.xml",
                                     "DebugIconSoundSource.xml",
                                     "DebugIconSoundListener.xml",
                                     "DebugIconZone.xml",
                                     "DebugIconSplinePathPoint.xml",
                                     "DebugIconCollisionTrigger.xml",
                                     "DebugIconCustomGeometry.xml",
                                     "DebugIconParticleEmitter.xml"};

Array<String> ComponentTypes = {"Light",
                                "Light",
                                "Light",
                                "Camera",
                                "SoundSource",
                                "SoundSource3D",
                                "SoundListener",
                                "Zone",
                                "SplinePath",
                                "RigidBody",
                                "CustomGeometry",
                                "ParticleEmitter"};

Array<BillboardSet@> debugIconsSet(ICON_COUNT);
Node@ debugIconsNode = null;
int stepDebugIconsUpdate = 100; //ms
int timeToNextDebugIconsUpdate = 0;
int stepDebugIconsUpdateSplinePath = 1000; //ms
int timeToNextDebugIconsUpdateSplinePath = 0;
const int splinePathResolution = 16;
const float splineStep = 1.0f / splinePathResolution;
bool debugIconsShow = true;
Vector2 debugIconsSize = Vector2(0.025, 0.025);
Vector2 debugIconsSizeSmall = debugIconsSize / 1.5;
Vector2 debugIconsSizeBig = debugIconsSize * 1.5;
Vector2 debugIconsMaxSize = debugIconsSize * 50;
VariantMap debugIconsPlacement;
const int debugIconsPlacementIndent = 1.0;
const float debugIconsOrthoDistance = 15.0f;

Vector2 Max(Vector2 a, Vector2 b)
{
    return (a.length > b.length ? a : b);
}

Vector2 Clamp(Vector2 a, Vector2 b)
{
    return Vector2((a.x > b.x ? b.x : a.x),(a.y > b.y ? b.y : a.y));
}

Vector2 ClampToIconMaxSize(Vector2 a)
{
    return Clamp(a, debugIconsMaxSize);
}

void CreateDebugIcons(Node@ tempNode)
{
    if (editorScene is null) return;
    debugIconsSet.Resize(ICON_COUNT);
    for (int i = 0; i < ICON_COUNT; i++)
    {
        debugIconsSet[i] = tempNode.CreateComponent("BillboardSet");
        debugIconsSet[i].material = cache.GetResource("Material", "Materials/Editor/" + IconsTypesMaterials[i]);
        debugIconsSet[i].sorted = true;
        debugIconsSet[i].temporary = true;
        debugIconsSet[i].viewMask = 0x80000000;
    }
}

void UpdateViewDebugIcons()
{
    if (editorScene is null || timeToNextDebugIconsUpdate > time.systemTime) return;

    debugIconsNode = editorScene.GetChild("DebugIconsContainer", true);

    if (debugIconsNode is null)
    {
        debugIconsNode = editorScene.CreateChild("DebugIconsContainer", LOCAL);
        debugIconsNode.temporary = true;
    }
    // Checkout if debugIconsNode do not have any BBS component, add all at once
    BillboardSet@ isBSExist = debugIconsNode.GetComponent("BillboardSet");
    if (isBSExist is null) CreateDebugIcons(debugIconsNode);

    if (debugIconsSet[ICON_POINT_LIGHT] !is null)
    {
        for(int i=0; i < ICON_COUNT; i++)
            debugIconsSet[i].enabled = debugIconsShow;
    }

    if (debugIconsShow == false) return;

    Vector3 camPos = activeViewport.cameraNode.worldPosition;
    bool isOrthographic = activeViewport.camera.orthographic;
    debugIconsPlacement.Clear();

    for(int iconType=0; iconType<ICON_COUNT; iconType++)
    {
        if(debugIconsSet[iconType] !is null)
        {
            // SplinePath update resolution
            if(iconType == ICON_SPLINE_PATH && timeToNextDebugIconsUpdateSplinePath > time.systemTime) continue;

            Array<Node@> nodes = editorScene.GetChildrenWithComponent(ComponentTypes[iconType], true);

            // Clear old data
            if(iconType == ICON_SPLINE_PATH)
                ClearCommit(ICON_SPLINE_PATH, ICON_SPLINE_PATH+1, nodes.length * splinePathResolution);
            else
                ClearCommit(iconType, iconType+1, nodes.length);

            if(nodes.length > 0)
            {

                // Fill with new data
                for(int i=0;i<nodes.length; i++)
                {
                    Component@ component = nodes[i].GetComponent(ComponentTypes[iconType]);
                    if (component is null) continue;

                    Billboard@ bb = null;
                    Color finalIconColor = debugIconsColors[ICON_COLOR_DEFAULT];
                    float distance = (camPos - nodes[i].worldPosition).length;
                    if (isOrthographic) distance = debugIconsOrthoDistance;
                    int iconsOffset = debugIconsPlacement[StringHash(nodes[i].id)].GetInt();
                    float iconsYPos = 0;

                    if(iconType==ICON_SPLINE_PATH)
                    {
                        SplinePath@ sp = cast<SplinePath>(component);
                        if(sp !is null)
                        {
                            for(int step=0; step < splinePathResolution; step++)
                            {
                                int index = (i * splinePathResolution) + step;
                                Vector3 splinePoint = sp.GetPoint(splineStep * step);
                                Billboard@ bb = debugIconsSet[ICON_SPLINE_PATH].billboards[index];
                                float stepDistance = (camPos - splinePoint).length;
                                if (isOrthographic) stepDistance = debugIconsOrthoDistance;

                                if(step == 0) // SplinePath start
                                {
                                    bb.color = debugIconsColors[ICON_COLOR_SPLINE_PATH_BEGIN];
                                    bb.size = ClampToIconMaxSize(Max(debugIconsSize * stepDistance, debugIconsSize));
                                    bb.position = splinePoint;
                                }
                                else if((step+1) >= (splinePathResolution - splineStep)) // SplinePath end
                                {
                                    bb.color = debugIconsColors[ICON_COLOR_SPLINE_PATH_END];
                                    bb.size = ClampToIconMaxSize(Max(debugIconsSize * stepDistance, debugIconsSize));
                                    bb.position = splinePoint;
                                }
                                else // SplinePath middle points
                                {
                                    bb.color = finalIconColor;
                                    bb.size = ClampToIconMaxSize(Max(debugIconsSizeSmall * stepDistance, debugIconsSizeSmall));
                                    bb.position = splinePoint;
                                }
                                bb.enabled = sp.enabled;
                                // Blend Icon relatively by distance to it
                                bb.color = Color(bb.color.r, bb.color.g, bb.color.b, 1.2f - 1.0f / (debugIconsMaxSize.x / bb.size.x));
                                if (bb.color.a < 0.25f) bb.enabled = false;
                            }
                        }
                    }
                    else
                    {
                        bb = debugIconsSet[iconType].billboards[i];
                        bb.size = ClampToIconMaxSize(Max(debugIconsSize * distance, debugIconsSize));

                        if(iconType==ICON_PARTICLE_EMITTER)
                        {
                            bb.size = ClampToIconMaxSize(Max(debugIconsSizeBig * distance, debugIconsSizeBig));
                        }
                        else if (iconType==ICON_TRIGGER)
                        {
                            RigidBody@ rigidbody = cast<RigidBody>(component);
                            if (rigidbody !is null)
                            {
                                if (rigidbody.trigger == false) continue;
                            }
                        }
                        else if (iconType==ICON_POINT_LIGHT || iconType==ICON_SPOT_LIGHT || iconType==ICON_DIRECTIONAL_LIGHT)
                        {
                            Light@ light = cast<Light>(component);
                            if(light !is null)
                            {
                                if(light.lightType == LIGHT_POINT)
                                    bb = debugIconsSet[ICON_POINT_LIGHT].billboards[i];
                                else if(light.lightType == LIGHT_DIRECTIONAL)
                                    bb = debugIconsSet[ICON_DIRECTIONAL_LIGHT].billboards[i];
                                else if(light.lightType == LIGHT_SPOT)
                                    bb = debugIconsSet[ICON_SPOT_LIGHT].billboards[i];

                                bb.size = ClampToIconMaxSize(Max(debugIconsSize * distance, debugIconsSize));
                                finalIconColor = light.effectiveColor;
                            }
                        }

                        bb.position = nodes[i].worldPosition;
                        // Blend Icon relatively by distance to it
                        bb.color = Color(finalIconColor.r, finalIconColor.g, finalIconColor.b, 1.2f - 1.0f / (debugIconsMaxSize.x / bb.size.x));
                        bb.enabled = component.enabled;
                        // Discard billboard if it almost transparent
                        if (bb.color.a < 0.25f) bb.enabled = false;
                        IncrementIconPlacement(bb.enabled, nodes[i], 1 );
                    }
                }
                Commit(iconType, iconType+1);
                // SplinePath update resolution
                if(iconType == ICON_SPLINE_PATH) timeToNextDebugIconsUpdateSplinePath = time.systemTime + stepDebugIconsUpdateSplinePath;
            }
        }
    }
    timeToNextDebugIconsUpdate = time.systemTime + stepDebugIconsUpdate;
}

void ClearCommit(int begin, int end, int newlength)
{
    for(int i=begin;i<end;i++)
    {
        debugIconsSet[i].numBillboards = 0;
        debugIconsSet[i].Commit();
        debugIconsSet[i].numBillboards = newlength;
    }
}

void Commit(int begin, int end)
{
    for(int i=begin;i<end;i++)
    {
        debugIconsSet[i].Commit();
    }
}

void IncrementIconPlacement(bool componentEnabled, Node@ node, int offset)
{
    if(componentEnabled == true)
    {
        int oldPlacement = debugIconsPlacement[StringHash(node.id)].GetInt();
        debugIconsPlacement[StringHash(node.id)] = Variant(oldPlacement + offset);
    }
}

// Editor debug icons
// to add new std debug icons just add IconType, IconsTypesMaterials and ComponentTypes items

enum IconsTypes
{
    ICON_POINT_LIGHT = 0,
    ICON_SPOT_LIGHT,
    ICON_DIRECTIONAL_LIGHT,
    ICON_CAMERA,
    ICON_SOUND_SOURCE,
    ICON_SOUND_SOURCE_3D,
    ICON_SOUND_LISTENERS,
    ICON_ZONE,
    ICON_SPLINE_PATH,
    ICON_TRIGGER,
    ICON_CUSTOM_GEOMETRY,
    ICON_PARTICLE_EMITTER,
    ICON_COUNT
}

enum IconsColorType
{
    ICON_COLOR_DEFAULT = 0,
    ICON_COLOR_SPLINE_PATH_BEGIN,
    ICON_COLOR_SPLINE_PATH_END
}

Array<Color> debugIconsColors = { Color(1,1,1) , Color(1,1,0), Color(0,1,0) };

Array<String> IconsTypesMaterials = {"DebugIconPointLight.xml",
                                     "DebugIconSpotLight.xml",
                                     "DebugIconLight.xml",
                                     "DebugIconCamera.xml",
                                     "DebugIconSoundSource.xml",
                                     "DebugIconSoundSource.xml",
                                     "DebugIconSoundListener.xml",
                                     "DebugIconZone.xml",
                                     "DebugIconSplinePathPoint.xml",
                                     "DebugIconCollisionTrigger.xml",
                                     "DebugIconCustomGeometry.xml",
                                     "DebugIconParticleEmitter.xml"};

Array<String> ComponentTypes = {"Light",
                                "Light",
                                "Light",
                                "Camera",
                                "SoundSource",
                                "SoundSource3D",
                                "SoundListener",
                                "Zone",
                                "SplinePath",
                                "RigidBody",
                                "CustomGeometry",
                                "ParticleEmitter"};

Array<BillboardSet@> debugIconsSet(ICON_COUNT);
Node@ debugIconsNode = null;
int stepDebugIconsUpdate = 100; //ms
int timeToNextDebugIconsUpdate = 0;
int stepDebugIconsUpdateSplinePath = 1000; //ms
int timeToNextDebugIconsUpdateSplinePath = 0;
const int splinePathResolution = 16;
const float splineStep = 1.0f / splinePathResolution;
bool debugIconsShow = true;
Vector2 debugIconsSize = Vector2(0.025, 0.025);
Vector2 debugIconsSizeSmall = debugIconsSize / 1.5;
Vector2 debugIconsSizeBig = debugIconsSize * 1.5;
Vector2 debugIconsMaxSize = debugIconsSize * 50;
VariantMap debugIconsPlacement;
const int debugIconsPlacementIndent = 1.0;
const float debugIconsOrthoDistance = 15.0f;

Vector2 Max(Vector2 a, Vector2 b)
{
    return (a.length > b.length ? a : b);
}

Vector2 Clamp(Vector2 a, Vector2 b)
{
    return Vector2((a.x > b.x ? b.x : a.x),(a.y > b.y ? b.y : a.y));
}

Vector2 ClampToIconMaxSize(Vector2 a)
{
    return Clamp(a, debugIconsMaxSize);
}

void CreateDebugIcons(Node@ tempNode)
{
    if (editorScene is null) return;
    debugIconsSet.Resize(ICON_COUNT);
    for (int i = 0; i < ICON_COUNT; i++)
    {
        debugIconsSet[i] = tempNode.CreateComponent("BillboardSet");
        debugIconsSet[i].material = cache.GetResource("Material", "Materials/Editor/" + IconsTypesMaterials[i]);
        debugIconsSet[i].sorted = true;
        debugIconsSet[i].temporary = true;
        debugIconsSet[i].viewMask = 0x80000000;
    }
}

void UpdateViewDebugIcons()
{
    if (editorScene is null || timeToNextDebugIconsUpdate > time.systemTime) return;

    debugIconsNode = editorScene.GetChild("DebugIconsContainer", true);

    if (debugIconsNode is null)
    {
        debugIconsNode = editorScene.CreateChild("DebugIconsContainer", LOCAL);
        debugIconsNode.temporary = true;
    }
    // Checkout if debugIconsNode do not have any BBS component, add all at once
    BillboardSet@ isBSExist = debugIconsNode.GetComponent("BillboardSet");
    if (isBSExist is null) CreateDebugIcons(debugIconsNode);

    if (debugIconsSet[ICON_POINT_LIGHT] !is null)
    {
        for(int i=0; i < ICON_COUNT; i++)
            debugIconsSet[i].enabled = debugIconsShow;
    }

    if (debugIconsShow == false) return;

    Vector3 camPos = activeViewport.cameraNode.worldPosition;
    bool isOrthographic = activeViewport.camera.orthographic;
    debugIconsPlacement.Clear();

    for(int iconType=0; iconType<ICON_COUNT; iconType++)
    {
        if(debugIconsSet[iconType] !is null)
        {
            // SplinePath update resolution
            if(iconType == ICON_SPLINE_PATH && timeToNextDebugIconsUpdateSplinePath > time.systemTime) continue;

            Array<Node@> nodes = editorScene.GetChildrenWithComponent(ComponentTypes[iconType], true);

            // Clear old data
            if(iconType == ICON_SPLINE_PATH)
                ClearCommit(ICON_SPLINE_PATH, ICON_SPLINE_PATH+1, nodes.length * splinePathResolution);
            else
                ClearCommit(iconType, iconType+1, nodes.length);

            if(nodes.length > 0)
            {

                // Fill with new data
                for(int i=0;i<nodes.length; i++)
                {
                    Component@ component = nodes[i].GetComponent(ComponentTypes[iconType]);
                    if (component is null) continue;

                    Billboard@ bb = null;
                    Color finalIconColor = debugIconsColors[ICON_COLOR_DEFAULT];
                    float distance = (camPos - nodes[i].worldPosition).length;
                    if (isOrthographic) distance = debugIconsOrthoDistance;
                    int iconsOffset = debugIconsPlacement[StringHash(nodes[i].id)].GetInt();
                    float iconsYPos = 0;

                    if(iconType==ICON_SPLINE_PATH)
                    {
                        SplinePath@ sp = cast<SplinePath>(component);
                        if(sp !is null)
                            if(sp.length > 0.01f)
                            {
                                for(int step=0; step < splinePathResolution; step++)
                                {
                                    int index = (i * splinePathResolution) + step;
                                    Vector3 splinePoint = sp.GetPoint(splineStep * step);
                                    Billboard@ bb = debugIconsSet[ICON_SPLINE_PATH].billboards[index];
                                    float stepDistance = (camPos - splinePoint).length;
                                    if (isOrthographic) stepDistance = debugIconsOrthoDistance;

                                    if(step == 0) // SplinePath start
                                    {
                                        bb.color = debugIconsColors[ICON_COLOR_SPLINE_PATH_BEGIN];
                                        bb.size = ClampToIconMaxSize(Max(debugIconsSize * stepDistance, debugIconsSize));
                                        bb.position = splinePoint;
                                    }
                                    else if((step+1) >= (splinePathResolution - splineStep)) // SplinePath end
                                    {
                                        bb.color = debugIconsColors[ICON_COLOR_SPLINE_PATH_END];
                                        bb.size = ClampToIconMaxSize(Max(debugIconsSize * stepDistance, debugIconsSize));
                                        bb.position = splinePoint;
                                    }
                                    else // SplinePath middle points
                                    {
                                        bb.color = finalIconColor;
                                        bb.size = ClampToIconMaxSize(Max(debugIconsSizeSmall * stepDistance, debugIconsSizeSmall));
                                        bb.position = splinePoint;
                                    }
                                    bb.enabled = sp.enabled;
                                    // Blend Icon relatively by distance to it
                                    bb.color = Color(bb.color.r, bb.color.g, bb.color.b, 1.2f - 1.0f / (debugIconsMaxSize.x / bb.size.x));
                                    if (bb.color.a < 0.25f) bb.enabled = false;
                                }
                            }
                    }
                    else
                    {
                        bb = debugIconsSet[iconType].billboards[i];
                        bb.size = ClampToIconMaxSize(Max(debugIconsSize * distance, debugIconsSize));

                        if(iconType==ICON_PARTICLE_EMITTER)
                        {
                            bb.size = ClampToIconMaxSize(Max(debugIconsSizeBig * distance, debugIconsSizeBig));
                        }
                        else if (iconType==ICON_TRIGGER)
                        {
                            RigidBody@ rigidbody = cast<RigidBody>(component);
                            if (rigidbody !is null)
                            {
                                if (rigidbody.trigger == false) continue;
                            }
                        }
                        else if (iconType==ICON_POINT_LIGHT || iconType==ICON_SPOT_LIGHT || iconType==ICON_DIRECTIONAL_LIGHT)
                        {
                            Light@ light = cast<Light>(component);
                            if(light !is null)
                            {
                                if(light.lightType == LIGHT_POINT)
                                    bb = debugIconsSet[ICON_POINT_LIGHT].billboards[i];
                                else if(light.lightType == LIGHT_DIRECTIONAL)
                                    bb = debugIconsSet[ICON_DIRECTIONAL_LIGHT].billboards[i];
                                else if(light.lightType == LIGHT_SPOT)
                                    bb = debugIconsSet[ICON_SPOT_LIGHT].billboards[i];

                                bb.size = ClampToIconMaxSize(Max(debugIconsSize * distance, debugIconsSize));
                                finalIconColor = light.effectiveColor;
                            }
                        }

                        bb.position = nodes[i].worldPosition;
                        // Blend Icon relatively by distance to it
                        bb.color = Color(finalIconColor.r, finalIconColor.g, finalIconColor.b, 1.2f - 1.0f / (debugIconsMaxSize.x / bb.size.x));
                        bb.enabled = component.enabled;
                        // Discard billboard if it almost transparent
                        if (bb.color.a < 0.25f) bb.enabled = false;
                        IncrementIconPlacement(bb.enabled, nodes[i], 1 );
                    }
                }
                Commit(iconType, iconType+1);
                // SplinePath update resolution
                if(iconType == ICON_SPLINE_PATH) timeToNextDebugIconsUpdateSplinePath = time.systemTime + stepDebugIconsUpdateSplinePath;
            }
        }
    }
    timeToNextDebugIconsUpdate = time.systemTime + stepDebugIconsUpdate;
}

void ClearCommit(int begin, int end, int newlength)
{
    for(int i=begin;i<end;i++)
    {
        debugIconsSet[i].numBillboards = 0;
        debugIconsSet[i].Commit();
        debugIconsSet[i].numBillboards = newlength;
    }
}

void Commit(int begin, int end)
{
    for(int i=begin;i<end;i++)
    {
        debugIconsSet[i].Commit();
    }
}

void IncrementIconPlacement(bool componentEnabled, Node@ node, int offset)
{
    if(componentEnabled == true)
    {
        int oldPlacement = debugIconsPlacement[StringHash(node.id)].GetInt();
        debugIconsPlacement[StringHash(node.id)] = Variant(oldPlacement + offset);
    }
}

add SplinePath check for length

add SplinePath check for length


package aerys.minko.type.enum
{
	import aerys.minko.ns.minko_render;

	import flash.display.TriangleCulling;
	import flash.display3D.Context3DTriangleFace;

	public final class TriangleCulling
	{
		public static const NONE	: int = 0;
		public static const BACK	: int = 1;
		public static const FRONT	: int = 2;
		public static const BOTH	: int = 3;

		minko_render static const STRINGS : Vector.<String> = new <String>[
			Context3DTriangleFace.NONE,
			Context3DTriangleFace.FRONT,
			Context3DTriangleFace.BACK,
			Context3DTriangleFace.FRONT_AND_BACK
		];
	}
}

package aerys.minko.type.enum
{
	import aerys.minko.ns.minko_render;

	import flash.display.TriangleCulling;
	import flash.display3D.Context3DTriangleFace;

	public final class TriangleCulling
	{
		public static const NONE	: uint = 0;
		public static const BACK	: uint = 1;
		public static const FRONT	: uint = 2;
		public static const BOTH	: uint = 3;

		minko_render static const STRINGS : Vector.<String> = new <String>[
			Context3DTriangleFace.NONE,
			Context3DTriangleFace.FRONT,
			Context3DTriangleFace.BACK,
			Context3DTriangleFace.FRONT_AND_BACK
		];
	}
}

use uint for the TriangleCulling enum values