Index: /trunk/sl1/README.HTML
===================================================================
--- /trunk/sl1/README.HTML (revision 1)
+++ /trunk/sl1/README.HTML (revision 1)
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+	<title>JW WMV Player</title>
+
+	<style type="text/css">
+		body { background-color: #fff; padding: 0 25px; color:#000; font: 13px/18px Arial, sans-serif; }
+		a { color: #360; }
+		h3 { padding-top: 50px; }
+	</style>
+
+</head>
+<body>
+
+	<h3>Example</h3>
+	<p>Here's a simple example of the <a href="http://www.jeroenwijering.com/?item=JW_WMV_Player">JW WMV Player</a> embedded in a page. Copy-paste the source code and put the files on your site to get started.</p>
+
+
+
+	<div id="container"></div>
+
+	<script type='text/javascript' src="silverlight.js"></script>
+	<script type='text/javascript' src="wmvplayer.js"></script>
+	<script type="text/javascript">
+		var cnt = document.getElementById("container");
+		var src = 'wmvplayer.xaml';
+		var cfg = {
+			file:'afraid.wmv',
+			image:'afraid.jpg',
+			height:'220',
+			width:'400'
+		};
+		var ply = new jeroenwijering.Player(cnt,src,cfg);
+	</script>
+
+
+
+	<h3>Quickstart</h3>
+	<p>The easiest way to get going with the wmvplayer is <a href="http://www.jeroenwijering.com/?page=wizard&example=41">by using the setup wizard</a>. Select an example, set the file or playlist you want to play and copy-paste the embed code to your site! A list of all available settings can be found <a href="http://www.jeroenwijering.com/?item=Supported_Flashvars" title="All Variables supported by the WMV Player">at the Supported Flashvars page</a>.</p>
+
+	<h3>Licensing</h3>
+	<p>The WMV Player is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/">Creative Commons License</a>. It allows you to use, modify and redistribute the script for free for noncommercial purposes. For corporate use, <a href="http://www.jeroenwijering.com/?page=order" title="Order commercial licenses">please apply for a 20 euros commercial license</a>!</p>
+
+</body>
+</html>
Index: /trunk/sl1/wmvplayer.js
===================================================================
--- /trunk/sl1/wmvplayer.js (revision 47)
+++ /trunk/sl1/wmvplayer.js (revision 1)
@@ -1,4 +1,4 @@
 /****************************************************************************
-* JW WMV Player version 1.1, created with M$ Silverlight 1.0
+* JW WMV Player version 1.0, created with M$ Silverlight 1.0
 *
 * This file contains all logic for the JW WMV Player. For a functional setup,
@@ -32,6 +32,5 @@
 	this.configuration = {
 		backgroundcolor:'FFFFFF',
-		windowless:'false',
-		file:'',
+		file:'video.wmv',
 		height:'260',
 		image:'',
@@ -43,4 +42,5 @@
 		logo:'',
 		overstretch:'false',
+		showicons:'true',
 		shownavigation:'true',
 		showstop:'false',
@@ -53,5 +53,4 @@
 		repeat:'false',
 		sender:'',
-		start:'0',
 		volume:'90',
 		link:'',
@@ -76,10 +75,9 @@
 			version:'1.0',
 			inplaceInstallPrompt:true,
-			isWindowless:this.configuration['windowless'],
+			isWindowless:'false',
 			background:'#'+this.configuration['backgroundcolor']
 		},
 		events:{
-			onLoad:this.onLoadHandler,
-			onError:null
+			onLoad:this.onLoadHandler
 		},
 		context:this
@@ -88,44 +86,10 @@
 
 jeroenwijering.Player.prototype = {
-	addListener: function(typ,fcn) {
-		this.view.listeners.push({type:typ,func:fcn});
-	},
-
-	getConfig: function() { 
-		return this.configuration;
-	},
-
 	onLoadHandler: function(pid,tgt,sdr) {
 		tgt.configuration['sender'] = sdr;
-		tgt.controller = new jeroenwijering.Controller(tgt.configuration);
-		tgt.view = new jeroenwijering.View(tgt.configuration,tgt.controller);
-		tgt.model = new jeroenwijering.Model(tgt.configuration,tgt.controller,tgt.view);
-		tgt.controller.startMVC(tgt.view,tgt.model);
-	},
-
-	sendEvent: function(typ,prm) {
-		switch(typ.toUpperCase()) {
-			case 'LINK':
-				this.controller.setLink();
-				break;
-			case 'LOAD':
-				this.controller.setLoad(prm);
-				break;
-			case 'MUTE':
-				this.controller.setMute();
-				break;
-			case 'PLAY':
-				this.controller.setPlay();
-				break;
-			case 'SCRUB':
-				this.controller.setScrub(prm);
-				break;
-			case 'STOP':
-				this.controller.setStop();
-				break;
-			case 'VOLUME':
-				this.controller.setVolume(prm);
-				break;
-		}
+		this.controller = new jeroenwijering.Controller(tgt.configuration);
+		this.view = new jeroenwijering.View(tgt.configuration,this.controller);
+		this.model = new jeroenwijering.Model(tgt.configuration,this.controller,this.view);
+		this.controller.startMVC(this.view,this.model);
 	}
 }
@@ -168,8 +132,4 @@
 	setState: function(old,stt) {
 		this.state = stt;
-		var pos = this.configuration['start'];
-		if(old == 'Closed' && pos > 0) {
-			setTimeout(jeroenwijering.utils.delegate(this,this.setScrub),200,pos);
-		} 
 	},
 
@@ -184,14 +144,4 @@
 	},
 
-	setLoad: function(fil) {
-		if(this.model.state != "Closed") {
-			this.model.goStop(); 
-		}
-		this.configuration['file'] = fil;
-		if(this.configuration['autostart'] == 'true') {
-			setTimeout(jeroenwijering.utils.delegate(this.model,this.model.goStart),100);
-		}
-	},
-
 	setMute: function() {
 		if(this.configuration['usemute'] == 'true') {
@@ -208,9 +158,5 @@
 	setPlay: function() {
 		if(this.state == 'Buffering' || this.state == 'Playing') {
-			if(this.configuration['duration'] == 0) { 
-				this.model.goStop();
-			} else { 
-				this.model.goPause();
-			}
+			this.model.goPause();
 		} else {
 			this.model.goStart();
@@ -219,5 +165,5 @@
 
 	setScrub: function(sec) {
-		if(sec < 2) {
+		if(sec < 2) { 
 			sec = 0;
 		} else if (sec > this.configuration['duration']-4) {
@@ -267,5 +213,4 @@
 jeroenwijering.View = function(cfg,ctr) {
 	this.configuration = cfg;
-	this.listeners = Array();
 	this.controller = ctr;
 	this.fstimeout;
@@ -290,5 +235,4 @@
 			snd.findName("BufferText").Text = pct;
 		}
-		this.delegate('BUFFER',[pct]);
 	},
 
@@ -312,5 +256,4 @@
 		}
 		this.resizePlayer();
-		this.delegate('FULLSCREEN');
 	},
 
@@ -336,5 +279,4 @@
 		var max = snd.findName("TimeSlider").Width;
 		snd.findName("DownloadProgress").Width = Math.round(max*pct/100);
-		this.delegate('LOAD',[pct]);
 	},
 
@@ -355,5 +297,4 @@
 			snd.findName("MuteIcon").Visibility = "Collapsed";
 		}
-		this.delegate('MUTE');
 	},
 
@@ -365,5 +306,5 @@
 			snd.findName("PlaySymbol").Visibility = "Collapsed";
 			snd.findName("PlayOffSymbol").Visibility = "Visible";
-			if (stt=='Playing') {
+			if (stt=='Playing' || this.configuration['showicons']=='false') {
 				snd.findName("BufferIcon").Visibility = "Collapsed";
 				snd.findName("BufferText").Visibility = "Collapsed";
@@ -371,7 +312,10 @@
 					snd.findName("MuteIcon").Visibility = "Visible";
 				}
-			} else{
+			} else if(this.configuration['showicons'] == 'true') {
 				snd.findName("BufferIcon").Visibility = "Visible";
 				snd.findName("BufferText").Visibility = "Visible";
+			} else {
+				snd.findName("BufferIcon").Visibility = "Collapsed";
+				snd.findName("BufferText").Visibility = "Collapsed";
 			}
 		} else { 
@@ -379,19 +323,12 @@
 			snd.findName("BufferIcon").Visibility = "Collapsed";
 			snd.findName("BufferText").Visibility = "Collapsed";
+			snd.findName("PlaySymbol").Visibility = "Visible";
 			snd.findName("PlayOffSymbol").Visibility = "Collapsed";
-			snd.findName("PlaySymbol").Visibility = "Visible";
-			if(this.configuration['linkfromdisplay'] == 'true') {
+			if(this.configuration['showicons'] == 'true') {
+				snd.findName("PlayIcon").Visibility = "Visible";
+			} else {
 				snd.findName("PlayIcon").Visibility = "Collapsed";
-			} else { 
-				snd.findName("PlayIcon").Visibility = "Visible";
 			}
 		}
-		try {
-			if(!(old == 'Completed' && stt == 'Buffering') &&
-				!(old == 'Buffering' && stt == 'Paused')) {
-				playerStatusChange(old.toUpperCase(),stt.toUpperCase());
-			}
-		} catch (err) {}
-		this.delegate('STATE',[old,stt]);
 	},
 
@@ -400,16 +337,13 @@
 		var snd = this.configuration['sender'];
 		var max = snd.findName("TimeSlider").Width;
-		if(dur > 0) {
-			var pos = Math.round(max*elp/dur);
-			this.configuration['duration'] = dur;
-			snd.findName("ElapsedText").Text = jeroenwijering.utils.timestring(elp);
-			snd.findName("RemainingText").Text = jeroenwijering.utils.timestring(dur-elp);
-			snd.findName("TimeSymbol").Visibility = "Visible";
-			snd.findName("TimeSymbol")['Canvas.Left'] = pos+4;
-			snd.findName("TimeHighlight").Width = pos-2;
-		} else  { 
-			snd.findName("TimeSymbol").Visibility = "Collapsed";
-		}
-		this.delegate('TIME',[elp,dur]);
+		var pos = Math.round(max*elp/dur);
+		if(isNaN(pos)) { pos = 0; }
+		this.configuration['duration'] = dur;
+		snd.findName("ElapsedText").Text = 
+			jeroenwijering.utils.timestring(elp);
+		snd.findName("RemainingText").Text = 
+			jeroenwijering.utils.timestring(dur-elp);
+		snd.findName("TimeSymbol")['Canvas.Left'] = pos+4;
+		snd.findName("TimeHighlight").Width = pos-2;
 	},
 
@@ -417,5 +351,4 @@
 		var snd = this.configuration['sender'];
 		snd.findName("VolumeHighlight").Width = Math.round(pct/5);
-		this.delegate('VOLUME',[pct]);
 	},
 
@@ -431,5 +364,4 @@
 				jeroenwijering.utils.delegate(this.controller,
 				this.controller.setLink));
-			this.display.findName("PlayIcon").Visibility = "Collapsed";
 		}
 		if(this.configuration['logo'] != '') {
@@ -487,12 +419,4 @@
 		this.configuration['sender'].findName(sld+'Symbol').Fill = 
 			"#FF"+this.configuration['frontcolor'];
-	},
-
-	delegate: function(typ,arg) {
-		for(var i=0; i<this.listeners.length; i++) {
-			if(this.listeners[i]['type'].toUpperCase() == typ) {
-				this.listeners[i]['func'].apply(null,arg);
-			}
-		}
 	},
 
@@ -559,6 +483,5 @@
 		this.centerElement('BufferIcon',wid,hei);
 		this.centerElement('BufferText',wid,hei);
-		this.display.findName('OverlayCanvas')['Canvas.Left'] = wid -
-			this.display.findName('OverlayCanvas').Width - 10;
+		this.display.findName('OverlayCanvas')['Canvas.Left'] = wid-110;
 		this.display.Visibility = "Visible";
 	},
@@ -699,4 +622,7 @@
 			this.video.Position = jeroenwijering.utils.spanstring(sec);
 		}
+		try { 
+			prr.hideAd();
+		} catch (err) {}
 	},
 
@@ -706,6 +632,4 @@
 		this.goPause(0);
 		this.video.Source = 'null';
-		this.view.onBuffer(0);
-		clearInterval(this.timeint);
 	},
 
@@ -735,6 +659,4 @@
 			this.goStart(0);
 		} else {
-			this.state = 'Completed';
-			this.view.onState(this.state,'Completed');
 			this.video.Visibility = 'Collapsed';
 			this.preview.Visibility = 'Visible';
Index: /trunk/sl1/wmvplayer.xaml
===================================================================
--- /trunk/sl1/wmvplayer.xaml (revision 42)
+++ /trunk/sl1/wmvplayer.xaml (revision 1)
@@ -1,5 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <!--
-JW WMV Player version 1.1, created with M$ Silverlight 1.0.
+JW WMV Player version 1.0, created with M$ Silverlight 1.0.
 
 This file contains all logic for the JW WMV Player. For a functional setup,
Index: /trunk/sl1/silverlight.js
===================================================================
--- /trunk/sl1/silverlight.js (revision 41)
+++ /trunk/sl1/silverlight.js (revision 1)
@@ -1,576 +1,15 @@
-﻿///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 //
-//  Silverlight.js   			version 2.0.30523.6
+//  Silverlight.js   			version 1.0
 //
 //  This file is provided by Microsoft as a helper file for websites that
-//  incorporate Silverlight Objects. This file is provided under the Microsoft
-//  Public License available at 
-//  http://code.msdn.microsoft.com/silverlightjs/Project/License.aspx.  
+//  incorporate Silverlight Objects. This file is provided under the Silverlight 
+//  SDK 1.0 license available at http://go.microsoft.com/fwlink/?linkid=94240.  
 //  You may not use or distribute this file or the code in this file except as 
 //  expressly permitted under that license.
 // 
-//  Copyright (c) Microsoft Corporation. All rights reserved.
+//  Copyright (c) 2007 Microsoft Corporation. All rights reserved.
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-if (!window.Silverlight)
-{
-    window.Silverlight = { };
-}
-
-//////////////////////////////////////////////////////////////////
-//
-// _silverlightCount:
-//
-// Counter of globalized event handlers
-//
-//////////////////////////////////////////////////////////////////
-Silverlight._silverlightCount = 0;
-
-//////////////////////////////////////////////////////////////////
-//
-// fwlinkRoot:
-//
-// Prefix for fwlink URL's
-//
-//////////////////////////////////////////////////////////////////
-Silverlight.fwlinkRoot='http://go2.microsoft.com/fwlink/?LinkID=';
-
-//////////////////////////////////////////////////////////////////
-//  
-// onGetSilverlight:
-//
-// Called by Silverlight.GetSilverlight to notify the page that a user
-// has requested the Silverlight installer
-//
-//////////////////////////////////////////////////////////////////
-Silverlight.onGetSilverlight = null;
-
-//////////////////////////////////////////////////////////////////
-//
-// onSilverlightInstalled:
-//
-// Called by Silverlight.WaitForInstallCompletion when the page detects
-// that Silverlight has been installed. The event handler is not called
-// in upgrade scenarios.
-//
-//////////////////////////////////////////////////////////////////
-Silverlight.onSilverlightInstalled = function () {window.location.reload(false);};
-
-//////////////////////////////////////////////////////////////////
-//
-// isInstalled:
-//
-// Checks to see if the correct version is installed
-//
-//////////////////////////////////////////////////////////////////
-Silverlight.isInstalled = function(version)
-{
-    var isVersionSupported=false;
-    var container = null;
-    
-    try 
-    {
-        var control = null;
-        
-        try
-        {
-            control = new ActiveXObject('AgControl.AgControl');
-            if ( version == null )
-            {
-                isVersionSupported = true;
-            }
-            else if ( control.IsVersionSupported(version) )
-            {
-                isVersionSupported = true;
-            }
-            control = null;
-        }
-        catch (e)
-        {
-            var plugin = navigator.plugins["Silverlight Plug-In"] ;
-            if ( plugin )
-            {
-                if ( version === null )
-                {
-                    isVersionSupported = true;
-                }
-                else
-                {
-                    var actualVer = plugin.description;
-                    if ( actualVer === "1.0.30226.2")
-                        actualVer = "2.0.30226.2";
-                    var actualVerArray =actualVer.split(".");
-                    while ( actualVerArray.length > 3)
-                    {
-                        actualVerArray.pop();
-                    }
-                    while ( actualVerArray.length < 4)
-                    {
-                        actualVerArray.push(0);
-                    }
-                    var reqVerArray = version.split(".");
-                    while ( reqVerArray.length > 4)
-                    {
-                        reqVerArray.pop();
-                    }
-                    
-                    var requiredVersionPart ;
-                    var actualVersionPart
-                    var index = 0;
-                    
-                    
-                    do
-                    {
-                        requiredVersionPart = parseInt(reqVerArray[index]);
-                        actualVersionPart = parseInt(actualVerArray[index]);
-                        index++;
-                    }
-                    while (index < reqVerArray.length && requiredVersionPart === actualVersionPart);
-                    
-                    if ( requiredVersionPart <= actualVersionPart && !isNaN(requiredVersionPart) )
-                    {
-                        isVersionSupported = true;
-                    }
-                }
-            }
-        }
-    }
-    catch (e) 
-    {
-        isVersionSupported = false;
-    }
-    if (container) 
-    {
-        document.body.removeChild(container);
-    }
-    
-    return isVersionSupported;
-}
-//////////////////////////////////////////////////////////////////
-//
-// WaitForInstallCompletion:
-//
-// Occasionally checks for Silverlight installation status. If it
-// detects that Silverlight has been installed then it calls
-// Silverlight.onSilverlightInstalled();. This is only supported
-// if Silverlight was not previously installed on this computer.
-//
-//////////////////////////////////////////////////////////////////
-Silverlight.WaitForInstallCompletion = function()
-{
-    if ( ! Silverlight.isBrowserRestartRequired && Silverlight.onSilverlightInstalled )
-    {
-        try
-        {
-            navigator.plugins.refresh();
-        }
-        catch(e)
-        {
-        }
-        if ( Silverlight.isInstalled(null) )
-        {
-            Silverlight.onSilverlightInstalled();
-        }
-        else
-        {
-              setTimeout(Silverlight.WaitForInstallCompletion, 3000);
-        }    
-    }
-}
-//////////////////////////////////////////////////////////////////
-//
-// __startup:
-//
-// Performs startup tasks
-//////////////////////////////////////////////////////////////////
-Silverlight.__startup = function()
-{
-    Silverlight.isBrowserRestartRequired = Silverlight.isInstalled(null);
-    if ( !Silverlight.isBrowserRestartRequired)
-    {
-        Silverlight.WaitForInstallCompletion();
-    }
-    if (window.removeEventListener) { 
-       window.removeEventListener('load', Silverlight.__startup , false);
-    }
-    else { 
-        window.detachEvent('onload', Silverlight.__startup );
-    }
-}
-
-if (window.addEventListener) 
-{
-    window.addEventListener('load', Silverlight.__startup , false);
-}
-else 
-{
-    window.attachEvent('onload', Silverlight.__startup );
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// createObject:
-//
-// Inserts a Silverlight <object> tag or installation experience into the HTML
-// DOM based on the current installed state of Silverlight. 
-//
-/////////////////////////////////////////////////////////////////////////////////
-
-Silverlight.createObject = function(source, parentElement, id, properties, events, initParams, userContext)
-{
-    var slPluginHelper = new Object();
-    var slProperties = properties;
-    var slEvents = events;
-    
-    slPluginHelper.version = slProperties.version;
-    slProperties.source = source;    
-    slPluginHelper.alt = slProperties.alt;
-    
-    //rename properties to their tag property names. For bacwards compatibility
-    //with Silverlight.js version 1.0
-    if ( initParams )
-        slProperties.initParams = initParams;
-    if ( slProperties.isWindowless && !slProperties.windowless)
-        slProperties.windowless = slProperties.isWindowless;
-    if ( slProperties.framerate && !slProperties.maxFramerate)
-        slProperties.maxFramerate = slProperties.framerate;
-    if ( id && !slProperties.id)
-        slProperties.id = id;
-    
-    // remove elements which are not to be added to the instantiation tag
-    delete slProperties.ignoreBrowserVer;
-    delete slProperties.inplaceInstallPrompt;
-    delete slProperties.version;
-    delete slProperties.isWindowless;
-    delete slProperties.framerate;
-    delete slProperties.data;
-    delete slProperties.src;
-    delete slProperties.alt;
-
-
-    // detect that the correct version of Silverlight is installed, else display install
-
-    if (Silverlight.isInstalled(slPluginHelper.version))
-    {
-        //move unknown events to the slProperties array
-        for (var name in slEvents)
-        {
-            if ( slEvents[name])
-            {
-                if ( name == "onLoad" && typeof slEvents[name] == "function" && slEvents[name].length != 1 )
-                {
-                    var onLoadHandler = slEvents[name];
-                    slEvents[name]=function (sender){ return onLoadHandler(document.getElementById(id), userContext, sender)};
-                }
-                var handlerName = Silverlight.__getHandlerName(slEvents[name]);
-                if ( handlerName != null )
-                {
-                    slProperties[name] = handlerName;
-                    slEvents[name] = null;
-                }
-                else
-                {
-                    throw "typeof events."+name+" must be 'function' or 'string'";
-                }
-            }
-        }
-        slPluginHTML = Silverlight.buildHTML(slProperties);
-    }
-    //The control could not be instantiated. Show the installation prompt
-    else 
-    {
-        slPluginHTML = Silverlight.buildPromptHTML(slPluginHelper);
-    }
-
-    // insert or return the HTML
-    if(parentElement)
-    {
-        parentElement.innerHTML = slPluginHTML;
-    }
-    else
-    {
-        return slPluginHTML;
-    }
-
-}
-
-///////////////////////////////////////////////////////////////////////////////
-//
-//  buildHTML:
-//
-//  create HTML that instantiates the control
-//
-///////////////////////////////////////////////////////////////////////////////
-Silverlight.buildHTML = function( slProperties)
-{
-    var htmlBuilder = [];
-
-    htmlBuilder.push('<object type=\"application/x-silverlight\" data="data:application/x-silverlight,"');
-    if ( slProperties.id != null )
-    {
-        htmlBuilder.push(' id="' + slProperties.id + '"');
-    }
-    if ( slProperties.width != null )
-    {
-        htmlBuilder.push(' width="' + slProperties.width+ '"');
-    }
-    if ( slProperties.height != null )
-    {
-        htmlBuilder.push(' height="' + slProperties.height + '"');
-    }
-    htmlBuilder.push(' >');
-    
-    delete slProperties.id;
-    delete slProperties.width;
-    delete slProperties.height;
-    
-    for (var name in slProperties)
-    {
-        if (slProperties[name])
-        {
-            htmlBuilder.push('<param name="'+Silverlight.HtmlAttributeEncode(name)+'" value="'+Silverlight.HtmlAttributeEncode(slProperties[name])+'" />');
-        }
-    }
-    htmlBuilder.push('<\/object>');
-    return htmlBuilder.join('');
-}
-
-
-
-//////////////////////////////////////////////////////////////////
-//
-// createObjectEx:
-//
-// takes a single parameter of all createObject 
-// parameters enclosed in {}
-//
-//////////////////////////////////////////////////////////////////
-
-Silverlight.createObjectEx = function(params)
-{
-    var parameters = params;
-    var html = Silverlight.createObject(parameters.source, parameters.parentElement, parameters.id, parameters.properties, parameters.events, parameters.initParams, parameters.context);
-    if (parameters.parentElement == null)
-    {
-        return html;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-//
-// buildPromptHTML
-//
-// Builds the HTML to prompt the user to download and install Silverlight
-//
-///////////////////////////////////////////////////////////////////////////////////////////////
-Silverlight.buildPromptHTML = function(slPluginHelper)
-{
-    var slPluginHTML = "";
-    var urlRoot = Silverlight.fwlinkRoot;
-    var shortVer = slPluginHelper.version ;
-    if ( slPluginHelper.alt )
-    {
-        slPluginHTML = slPluginHelper.alt;
-    }
-    else
-    {
-        if (! shortVer )
-        {
-            shortVer="";
-        }
-        slPluginHTML = "<a href='javascript:Silverlight.getSilverlight(\"{1}\");' style='text-decoration: none;'><img src='{2}' alt='Get Microsoft Silverlight' style='border-style: none'/></a>";
-        slPluginHTML = slPluginHTML.replace('{1}', shortVer );
-        slPluginHTML = slPluginHTML.replace('{2}', urlRoot + '108181');
-    }
-    
-    return slPluginHTML;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-//
-// getSilverlight:
-//
-// Navigates the browser to the appropriate Silverlight installer
-//
-///////////////////////////////////////////////////////////////////////////////////////////////
-Silverlight.getSilverlight = function(version)
-{
-    if (Silverlight.onGetSilverlight )
-    {
-        Silverlight.onGetSilverlight();
-    }
-    
-    var shortVer = "";
-    var reqVerArray = String(version).split(".");
-    if (reqVerArray.length > 1)
-    {
-        var majorNum = parseInt(reqVerArray[0] );
-        if ( isNaN(majorNum) || majorNum < 2 )
-        {
-            shortVer = "1.0";
-        }
-        else
-        {
-            shortVer = reqVerArray[0]+'.'+reqVerArray[1];
-        }
-    }
-    
-    var verArg = "";
-    
-    if (shortVer.match(/^\d+\056\d+$/) )
-    {
-        verArg = "&v="+shortVer;
-    }
-    
-    Silverlight.followFWLink("114576" + verArg);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-//
-// followFWLink:
-//
-// Navigates to a url based on fwlinkid
-//
-///////////////////////////////////////////////////////////////////////////////////////////////
-Silverlight.followFWLink = function(linkid)
-{
-    top.location=Silverlight.fwlinkRoot+String(linkid);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-//
-// HtmlAttributeEncode:
-//
-// Encodes special characters in input strings as charcodes
-//
-///////////////////////////////////////////////////////////////////////////////////////////////
-Silverlight.HtmlAttributeEncode = function( strInput )
-{
-      var c;
-      var retVal = '';
-
-    if(strInput == null)
-      {
-          return null;
-    }
-      
-      for(var cnt = 0; cnt < strInput.length; cnt++)
-      {
-            c = strInput.charCodeAt(cnt);
-
-            if (( ( c > 96 ) && ( c < 123 ) ) ||
-                  ( ( c > 64 ) && ( c < 91 ) ) ||
-                  ( ( c > 43 ) && ( c < 58 ) && (c!=47)) ||
-                  ( c == 95 ))
-            {
-                  retVal = retVal + String.fromCharCode(c);
-            }
-            else
-            {
-                  retVal = retVal + '&#' + c + ';';
-            }
-      }
-      
-      return retVal;
-}
-///////////////////////////////////////////////////////////////////////////////
-//
-//  default_error_handler:
-//
-//  Default error handling function 
-//
-///////////////////////////////////////////////////////////////////////////////
-
-Silverlight.default_error_handler = function (sender, args)
-{
-    var iErrorCode;
-    var errorType = args.ErrorType;
-
-    iErrorCode = args.ErrorCode;
-
-    var errMsg = "\nSilverlight error message     \n" ;
-
-    errMsg += "ErrorCode: "+ iErrorCode + "\n";
-
-
-    errMsg += "ErrorType: " + errorType + "       \n";
-    errMsg += "Message: " + args.ErrorMessage + "     \n";
-
-    if (errorType == "ParserError")
-    {
-        errMsg += "XamlFile: " + args.xamlFile + "     \n";
-        errMsg += "Line: " + args.lineNumber + "     \n";
-        errMsg += "Position: " + args.charPosition + "     \n";
-    }
-    else if (errorType == "RuntimeError")
-    {
-        if (args.lineNumber != 0)
-        {
-            errMsg += "Line: " + args.lineNumber + "     \n";
-            errMsg += "Position: " +  args.charPosition + "     \n";
-        }
-        errMsg += "MethodName: " + args.methodName + "     \n";
-    }
-    alert (errMsg);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-//
-// __cleanup:
-//
-// Releases event handler resources when the page is unloaded
-//
-///////////////////////////////////////////////////////////////////////////////////////////////
-Silverlight.__cleanup = function ()
-{
-    for (var i = Silverlight._silverlightCount - 1; i >= 0; i--) {
-        window['__slEvent' + i] = null;
-    }
-    Silverlight._silverlightCount = 0;
-    if (window.removeEventListener) { 
-       window.removeEventListener('unload', Silverlight.__cleanup , false);
-    }
-    else { 
-        window.detachEvent('onunload', Silverlight.__cleanup );
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-//
-// __getHandlerName:
-//
-// Generates named event handlers for delegates.
-//
-///////////////////////////////////////////////////////////////////////////////////////////////
-Silverlight.__getHandlerName = function (handler)
-{
-    var handlerName = "";
-    if ( typeof handler == "string")
-    {
-        handlerName = handler;
-    }
-    else if ( typeof handler == "function" )
-    {
-        if (Silverlight._silverlightCount == 0)
-        {
-            if (window.addEventListener) 
-            {
-                window.addEventListener('onunload', Silverlight.__cleanup , false);
-            }
-            else 
-            {
-                window.attachEvent('onunload', Silverlight.__cleanup );
-            }
-        }
-        var count = Silverlight._silverlightCount++;
-        handlerName = "__slEvent"+count;
-        
-        window[handlerName]=handler;
-    }
-    else
-    {
-        handlerName = null;
-    }
-    return handlerName;
-}
+if(!window.Silverlight)window.Silverlight={};Silverlight._silverlightCount=0;Silverlight.ua=null;Silverlight.available=false;Silverlight.fwlinkRoot="http://go.microsoft.com/fwlink/?LinkID=";Silverlight.detectUserAgent=function(){var a=window.navigator.userAgent;Silverlight.ua={OS:"Unsupported",Browser:"Unsupported"};if(a.indexOf("Windows NT")>=0)Silverlight.ua.OS="Windows";else if(a.indexOf("PPC Mac OS X")>=0)Silverlight.ua.OS="MacPPC";else if(a.indexOf("Intel Mac OS X")>=0)Silverlight.ua.OS="MacIntel";if(Silverlight.ua.OS!="Unsupported")if(a.indexOf("MSIE")>=0){if(navigator.userAgent.indexOf("Win64")==-1)if(parseInt(a.split("MSIE")[1])>=6)Silverlight.ua.Browser="MSIE"}else if(a.indexOf("Firefox")>=0){var b=a.split("Firefox/")[1].split("."),c=parseInt(b[0]);if(c>=2)Silverlight.ua.Browser="Firefox";else{var d=parseInt(b[1]);if(c==1&&d>=5)Silverlight.ua.Browser="Firefox"}}else if(a.indexOf("Safari")>=0)Silverlight.ua.Browser="Safari"};Silverlight.detectUserAgent();Silverlight.isInstalled=function(d){var c=false,a=null;try{var b=null;if(Silverlight.ua.Browser=="MSIE")b=new ActiveXObject("AgControl.AgControl");else if(navigator.plugins["Silverlight Plug-In"]){a=document.createElement("div");document.body.appendChild(a);if(Silverlight.ua.Browser=="Safari")a.innerHTML='<embed type="application/x-silverlight" />';else a.innerHTML='<object type="application/x-silverlight"  data="data:," />';b=a.childNodes[0]}document.body.innerHTML;if(b.IsVersionSupported(d))c=true;b=null;Silverlight.available=true}catch(e){c=false}if(a)document.body.removeChild(a);return c};Silverlight.createObject=function(l,g,m,j,k,i,h){var b={},a=j,c=k;a.source=l;b.parentElement=g;b.id=Silverlight.HtmlAttributeEncode(m);b.width=Silverlight.HtmlAttributeEncode(a.width);b.height=Silverlight.HtmlAttributeEncode(a.height);b.ignoreBrowserVer=Boolean(a.ignoreBrowserVer);b.inplaceInstallPrompt=Boolean(a.inplaceInstallPrompt);var e=a.version.split(".");b.shortVer=e[0]+"."+e[1];b.version=a.version;a.initParams=i;a.windowless=a.isWindowless;a.maxFramerate=a.framerate;for(var d in c)if(c[d]&&d!="onLoad"&&d!="onError"){a[d]=c[d];c[d]=null}delete a.width;delete a.height;delete a.id;delete a.onLoad;delete a.onError;delete a.ignoreBrowserVer;delete a.inplaceInstallPrompt;delete a.version;delete a.isWindowless;delete a.framerate;delete a.data;delete a.src;if(Silverlight.isInstalled(b.version)){if(Silverlight._silverlightCount==0)if(window.addEventListener)window.addEventListener("onunload",Silverlight.__cleanup,false);else window.attachEvent("onunload",Silverlight.__cleanup);var f=Silverlight._silverlightCount++;a.onLoad="__slLoad"+f;a.onError="__slError"+f;window[a.onLoad]=function(a){if(c.onLoad)c.onLoad(document.getElementById(b.id),h,a)};window[a.onError]=function(a,b){if(c.onError)c.onError(a,b);else Silverlight.default_error_handler(a,b)};slPluginHTML=Silverlight.buildHTML(b,a)}else slPluginHTML=Silverlight.buildPromptHTML(b);if(b.parentElement)b.parentElement.innerHTML=slPluginHTML;else return slPluginHTML};Silverlight.supportedUserAgent=function(){var a=Silverlight.ua,b=a.OS=="Unsupported"||a.Browser=="Unsupported"||a.OS=="Windows"&&a.Browser=="Safari"||a.OS.indexOf("Mac")>=0&&a.Browser=="IE";return !b};Silverlight.buildHTML=function(c,d){var a=[],e,i,g,f,h;if(Silverlight.ua.Browser=="Safari"){a.push("<embed ");e="";i=" ";g='="';f='"';h=' type="application/x-silverlight"/>'+"<iframe style='visibility:hidden;height:0;width:0'/>"}else{a.push('<object type="application/x-silverlight" data="data:,"');e=">";i=' <param name="';g='" value="';f='" />';h="</object>"}a.push(' id="'+c.id+'" width="'+c.width+'" height="'+c.height+'" '+e);for(var b in d)if(d[b])a.push(i+Silverlight.HtmlAttributeEncode(b)+g+Silverlight.HtmlAttributeEncode(d[b])+f);a.push(h);return a.join("")};Silverlight.default_error_handler=function(e,b){var d,c=b.ErrorType;d=b.ErrorCode;var a="\nSilverlight error message     \n";a+="ErrorCode: "+d+"\n";a+="ErrorType: "+c+"       \n";a+="Message: "+b.ErrorMessage+"     \n";if(c=="ParserError"){a+="XamlFile: "+b.xamlFile+"     \n";a+="Line: "+b.lineNumber+"     \n";a+="Position: "+b.charPosition+"     \n"}else if(c=="RuntimeError"){if(b.lineNumber!=0){a+="Line: "+b.lineNumber+"     \n";a+="Position: "+b.charPosition+"     \n"}a+="MethodName: "+b.methodName+"     \n"}alert(a)};Silverlight.createObjectEx=function(b){var a=b,c=Silverlight.createObject(a.source,a.parentElement,a.id,a.properties,a.events,a.initParams,a.context);if(a.parentElement==null)return c};Silverlight.buildPromptHTML=function(l){var a=null,d=Silverlight.fwlinkRoot,c=Silverlight.ua.OS,b="92822",e,f="Get Microsoft Silverlight",m="0x409";if(l.inplaceInstallPrompt){var n="98109",i;if(Silverlight.available){e="96189";i="96422"}else{e="96188";i="96422"}var h="93481",g="93483";if(c=="Windows"){b="92799";h="92803";g="92805"}else if(c=="MacIntel"){b="92808";h="92804";g="92806"}else if(c=="MacPPC"){b="92807";h="92815";g="92816"}var k='By clicking <b>"Get Microsoft Silverlight"</b> you accept the<br /><a title="Silverlight License Agreement" href="{2}" target="_top" style="text-decoration: underline; color: #96C5E1"><b>Silverlight license agreement</b></a>',j='Silverlight updates automatically, <a title="Silverlight Privacy Statement" href="{3}" target="_top" style="text-decoration: underline; color: #96C5E1"><b>learn more</b></a>';a='<table border="0" cellpadding="0" cellspacing="0" width="206px"><tr><td><img style="display: block; cursor: pointer; border= 0;" title="'+f+'" alt="'+f+'" onclick="javascript:Silverlight.followFWLink({0});" src="{1}" /></td></tr><tr><td style="width: 206px; margin: 0px; background: #FFFFFF; color: #C7C7C7; text-align: left; border-left-style: solid; border-right-style: solid; padding-left: 6px; padding-right: 6px; padding-top: 3px; padding-bottom: 0px; border-width: 2px; border-color: #c7c7bd; font-family: Verdana; font-size: 55%">'+k+'</td></tr><tr><td><img src="{5}" style="border: 0; display: block" /></td></tr><tr><td style="width: 206px; margin: 0px; background: #D8EFF9; color: #C7C7C7; text-align: left; border-left-style: solid; border-right-style: solid; padding-left: 6px; padding-right: 6px; padding-top: 0px; padding-bottom: 2px; border-width: 2px; border-color: #c7c7bd; font-family: Verdana; font-size: 55%">'+j+'</td></tr><tr><td><img alt="" src="{4}" /></td></tr></table>';a=a.replace("{2}",d+h);a=a.replace("{3}",d+g);a=a.replace("{4}",d+i);a=a.replace("{5}",d+n)}else{if(Silverlight.available)e="94377";else e="92801";if(c=="Windows")b="92800";else if(c=="MacIntel")b="92812";else if(c=="MacPPC")b="92811";a='<div style="display:block; width: 205px; height: 67px;"><img onclick="javascript:Silverlight.followFWLink({0});" style="border:0; cursor:pointer" src="{1}" title="'+f+'" alt="'+f+'"/></div>'}a=a.replace("{0}",b);a=a.replace("{1}",d+e+"&amp;clcid="+m);return a};Silverlight.__cleanup=function(){for(var a=Silverlight._silverlightCount-1;a>=0;a--){window["__slLoad"+a]=null;window["__slError"+a]=null}if(window.removeEventListener)window.removeEventListener("unload",Silverlight.__cleanup,false);else window.detachEvent("onunload",Silverlight.__cleanup)};Silverlight.followFWLink=function(a){top.location=Silverlight.fwlinkRoot+String(a)};Silverlight.HtmlAttributeEncode=function(c){var a,b="";if(c==null)return null;for(var d=0;d<c.length;d++){a=c.charCodeAt(d);if(a>96&&a<123||a>64&&a<91||a>43&&a<58&&a!=47||a==95)b=b+String.fromCharCode(a);else b=b+"&#"+a+";"}return b}
Index: /unk/sl1/readme.html
===================================================================
--- /trunk/sl1/readme.html (revision 45)
+++  (revision )
@@ -1,79 +1,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-	<title>JW Player for Silverlight</title>
-
-	<style type="text/css">
-		body { background-color: #fff; padding: 0 20px; color:#000; font: 13px/18px Arial, sans-serif; }
-		a { color: #360; }
-		h3 { padding-top: 20px; }
-		ol { margin:5px 0 15px 16px; padding:0; list-style-type:square; }
-	</style>
-
-</head>
-<body>
-
-	<h3>Example</h3>
-	<p>Thanks for downloading! Below you see a simple embedded example of the <a href="http://www.jeroenwijering.com/?item=JW_WMV_Player">JW Player</a>. 
-		Copy-paste the source code and put the .JS, .XAML and .WMV files on your site to get started.
-	</p>
-
-
-
-
-
-
-	<div name="mediaspace" id="mediaspace"></div>
-	<script type='text/javascript' src="silverlight.js"></script>
-	<script type='text/javascript' src="wmvplayer.js"></script>
-	<script type="text/javascript">
-		var cnt = document.getElementById("mediaspace");
-		var src = 'wmvplayer.xaml';
-		var cfg = {
-			file:'video.wmv',
-			image:'preview.jpg',
-			height:'240',
-			width:'440'
-		};
-		var ply = new jeroenwijering.Player(cnt,src,cfg);
-	</script>
-
-
-
-
-
-
-	<h3>Licensing</h3>
-	<p>The WMV Player is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/">Creative Commons License</a>. 
-		It allows you to use, modify and redistribute the script, but only for <b>noncommercial</b> purposes. 
-		For corporate use, <a href="http://www.jeroenwijering.com/?page=order">please apply for a commercial license</a>.
-	</p>
-
-
-	<h3>Quickstart</h3>
-	<p>The easiest way to get to know the player is by using <a href="http://www.jeroenwijering.com/?page=wizard">the setup wizard</a>.
-		Select an example, set the file or playlist you want to play and copy-paste the embed code to your site.
-	</p>
-
-
-	<h3>Wiki</h3>
-	<p>The <a href="http://code.longtailvideo.com/trac">JW Player Wiki</a> contains a wealth of information about the player, including:</p>
-	<ol>
-		<li>All <a href="http://code.longtailvideo.com/trac/wiki/SLFormats">supported file formats</a> (and playlists).</li>
-		<li>All <a href="http://code.longtailvideo.com/trac/wiki/SLVars">supported variables</a> for customizing the player.</li>
-		<li>The <a href="http://code.longtailvideo.com/trac/wiki/SLAPI">javascript API</a> documentation.</li>
-		<li>All <a href="http://code.longtailvideo.com/trac">previous versions</a> of the player.</li>
-	</ol>
-
-
-	<h3>Next steps</h3>
-	<p>If you're ready to get professional about online video, check out these two services that take your site to the next level:</p>
-	<ol>
-		<li><a href="http://www.longtailvideo.com">LongTail Video</a> is a no-nonsense video advertisement network that will increase your traffic and monetize your streams. <a href="http://www.longtailvideo.com.com/signup.asp">Sign up</a>.</li>
-		<li><a href="http://www.bitsontherun.com">Bits on the Run</a> is a hassle-free video management system that handles the encoding, streaming and tracking of your videos. <a href="http://www.bitsontherun.com.com/signup">Sign up</a>.</li>
-	</ol>
-
-
-</body>
-</html>
Index: /trunk/sl2/SilverlightToolbox/SilverlightToolbox.csproj.user
===================================================================
--- /trunk/sl2/SilverlightToolbox/SilverlightToolbox.csproj.user (revision 1)
+++ /trunk/sl2/SilverlightToolbox/SilverlightToolbox.csproj.user (revision 1)
@@ -0,0 +1,26 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties>
+          <StartPageUrl>
+          </StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightToolbox/Xml/SimpleXmlElement.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Xml/SimpleXmlElement.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Xml/SimpleXmlElement.cs (revision 1)
@@ -0,0 +1,265 @@
+﻿//------------------------------------------------------------------------------
+// SimpleXmlElement.cs
+// Jeff Wilcox <http://blogs.msdn.com/JeffWilcox>
+//------------------------------------------------------------------------------
+
+namespace SilverlightToolbox.Xml
+//namespace JeffWilcox.FX.Silverlight.Xml
+{
+    using System;
+    using System.IO;
+    using System.Text;
+    using System.Xml;
+    using System.Collections.Generic;
+
+    /// <summary>
+    /// Node in a DOM tree.  Similar to the standard .NET XmlElement, 
+    /// but simplified.
+    /// </summary>
+    public class SimpleXmlElement
+    {
+        /// <summary>
+        /// Tag Name
+        /// </summary>
+        private string _tagName;
+
+        /// <summary>
+        /// Contained text, if any
+        /// </summary>
+        private string _text;
+
+        /// <summary>
+        /// Element attributes
+        /// </summary>
+        private Dictionary<string, string> _attributes;
+
+        /// <summary>
+        /// Child nodes
+        /// </summary>
+        private List<SimpleXmlElement> _children;
+
+        /// <summary>
+        /// Keep memory consumption down by defaulting to a small size 
+        /// for the attribute dictionary
+        /// </summary>
+        private const int DefaultAttributeAssumption = 2;
+
+        /// <summary>
+        /// Size for new child lists 
+        /// </summary>
+        private const int DefaultChildrenAssumption = 1;
+
+        /// <summary>
+        /// Reference to a parent object, if any
+        /// </summary>
+        internal SimpleXmlElement _parent;
+
+        /// <summary>
+        /// Constructor, must be created internally (SimpleXmlDocument)
+        /// </summary>
+        internal SimpleXmlElement(string tagName)
+        {
+            _tagName = tagName;
+            _text = string.Empty;
+            _attributes = new Dictionary<string, string>(DefaultAttributeAssumption);
+            _children = new List<SimpleXmlElement>(DefaultChildrenAssumption);
+            _parent = null;
+        }
+
+        /// <summary>
+        /// Constructor, must be created internally (SimpleXmlDocument)
+        /// </summary>
+        internal SimpleXmlElement(string tagName, string text)
+            : this(tagName)
+        {
+            _text = text;
+        }
+
+        /// <summary>
+        /// Element tag name as parsed by XmlReader
+        /// </summary>
+        public string TagName
+        {
+            get { return _tagName; }
+        }
+
+        /// <summary>
+        /// Contained text, if any, for the element
+        /// </summary>
+        public string Text
+        {
+            get { return _text; }
+        }
+
+        /// <summary>
+        /// Override the text in the node
+        /// </summary>
+        internal void SetText(string text)
+        {
+            _text = text;
+        }
+
+        /// <summary>
+        /// Whether the element has any text
+        /// </summary>
+        public bool HasText
+        {
+            get { return _text.Length > 0; }
+        }
+
+        /// <summary>
+        /// Whether the element has any children
+        /// </summary>
+        public bool HasChildren
+        {
+            get { return _children.Count > 0; }
+        }
+
+        /// <summary>
+        /// Whether the element has any attributes
+        /// </summary>
+        public bool HasAttributes
+        {
+            get { return _attributes.Count > 0; }
+        }
+
+        /// <summary>
+        /// Append a child element and assign its parent reference
+        /// </summary>
+        internal void AppendChild(SimpleXmlElement child)
+        {
+            child._parent = this;
+            _children.Add(child);
+        }
+
+        /// <summary>
+        /// Return the parent element
+        /// </summary>
+        public SimpleXmlElement Parent
+        {
+            get { return _parent; }
+        }
+
+        /// <summary>
+        /// Return the attributes dictionary
+        /// </summary>
+        public IDictionary<string, string> Attributes
+        {
+            get { return _attributes; }
+        }
+
+        /// <summary>
+        /// Return the attributes list
+        /// </summary>
+        public IList<SimpleXmlElement> Children
+        {
+            get { return _children; }
+        }
+
+        /// <summary>
+        /// Simple tagname and # of children as the ToString, 
+        /// only useful for debugging.
+        /// </summary>
+        public override string ToString()
+        {
+            return _tagName + " with " + _children.Count.ToString() + " siblings";
+        }
+
+        /// <summary>
+        /// Effectively the XmlElement::OuterHTML
+        /// </summary>
+        public string ToXmlString()
+        {
+            return ToXmlString(0);
+        }
+
+        /// <summary>
+        /// Get a list of elements 
+        /// </summary>
+        public IList<SimpleXmlElement> GetElementsByTagName(string tagName)
+        {
+            List<SimpleXmlElement> elements = new List<SimpleXmlElement>();
+            if (_tagName == tagName)
+            {
+                elements.Add(this);
+            }
+            foreach (SimpleXmlElement child in _children)
+            {
+                elements.AddRange(child.GetElementsByTagName(tagName));
+            }
+            return elements;
+        }
+
+        /// <summary>
+        /// Get a single element by tag name
+        /// </summary>
+        /// <remarks>Be forewarned, if there are >1 tags you'll receive an exception</remarks>
+        public SimpleXmlElement GetElementByTagName(string singleTagName)
+        {
+            IList<SimpleXmlElement> tags = GetElementsByTagName(singleTagName);
+            if (tags.Count == 1)
+                return tags[0];
+            if (tags.Count > 1)
+                throw new InvalidOperationException("Multiple " + singleTagName + " tags were located.");
+            return null;
+        }
+
+        /// <summary>
+        /// Return the contained text of a single node by tag name
+        /// </summary>
+        public string GetTextOfSingleTagName(string singleTagName)
+        {
+            SimpleXmlElement tag = GetElementByTagName(singleTagName);
+            return (tag != null) ? tag.Text : null;
+        }
+
+        /// <summary>
+        /// Effectively the XmlElement::OuterHTML
+        /// </summary>
+        private string ToXmlString(int indent)
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < indent; ++i) sb.Append("\t");
+
+            sb.Append("<");
+            sb.Append(_tagName);
+
+            if (HasAttributes)
+            {
+                foreach (string key in _attributes.Keys)
+                {
+                    sb.Append(" ");
+                    sb.Append(key);
+                    sb.Append("=");
+                    sb.Append("\"");
+                    sb.Append(_attributes[key]);
+                    sb.Append("\"");
+                }
+            }
+
+            if (HasChildren || HasText)
+            {
+                sb.AppendLine(">");
+                foreach (SimpleXmlElement node in _children)
+                    sb.AppendLine(node.ToXmlString(indent + 1));
+                if (_text.Length > 0)
+                {
+                    sb.AppendLine();
+                    for (int i = 0; i < indent; ++i) sb.Append("\t");
+                    sb.Append("\t");
+                    sb.AppendLine(_text);
+                }
+                for (int i = 0; i < indent; ++i) sb.Append("\t");
+
+                sb.Append("</");
+                sb.Append(_tagName);
+                sb.Append(">");
+            }
+            else
+                sb.AppendLine(" />");
+
+            return sb.ToString();
+        }
+    }
+
+}
Index: /trunk/sl2/SilverlightToolbox/Xml/SimpleXmlDocument.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Xml/SimpleXmlDocument.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Xml/SimpleXmlDocument.cs (revision 1)
@@ -0,0 +1,153 @@
+﻿//------------------------------------------------------------------------------
+// SimpleXmlDocument.cs
+// Jeff Wilcox <http://blogs.msdn.com/JeffWilcox>
+//------------------------------------------------------------------------------
+
+namespace SilverlightToolbox.Xml
+//namespace JeffWilcox.FX.Silverlight.Xml
+{
+    using System;
+    using System.Collections.Generic;
+    using System.IO;
+    using System.Text;
+    using System.Xml;
+
+    /// <summary>
+    /// A very simple in-memory representation of an XML document.  Does not 
+    /// support many key XML needs and will not preserve a document during a 
+    /// roundtrip.
+    /// 
+    /// Not implemented include 
+    /// - Built-in namespace support
+    /// - PIs
+    /// - CDATA
+    /// - Document type or declarations
+    /// - Comments
+    /// 
+    /// What you do get includes
+    /// - Element names ["TagName"]
+    /// - Children
+    /// - Parent [null for root]
+    /// 
+    /// This was not designed to be a replacement for richer .NET XmlDocument 
+    /// classes, nor to be compatible with their syntax.
+    /// </summary>
+    public class SimpleXmlDocument
+    {
+        /// <summary>
+        /// Root element
+        /// </summary>
+        private SimpleXmlElement _root;
+
+        /// <summary>
+        /// Simple XML document constructor
+        /// </summary>
+        public SimpleXmlDocument()
+        {
+            _root = null;
+        }
+
+        /// <summary>
+        /// Simple XML document constructor
+        /// </summary>
+        public SimpleXmlDocument(string xmlString)
+            : this()
+        {
+            ParseString(xmlString);
+        }
+
+        /// <summary>
+        /// Static creation method
+        /// </summary>
+        public static SimpleXmlDocument Create(string xmlString)
+        {
+            SimpleXmlDocument doc = new SimpleXmlDocument(xmlString);
+            return doc;
+        }
+
+        /// <summary>
+        /// Parse a string containing XML (uses System.Xml.XmlReader) 
+        /// and populate the object.
+        /// </summary>
+        public void ParseString(string xmlString)
+        {
+            using (StringReader sr = new StringReader(xmlString))
+            {
+                XmlReader reader = XmlReader.Create(sr);
+                _root = ParseReader(reader);
+            }
+        }
+
+        /// <summary>
+        /// Root element
+        /// </summary>
+        public SimpleXmlElement Root
+        {
+            get
+            {
+                return _root;
+            }
+        }
+
+        /// <summary>
+        /// Use a forward, read-only reader to fill the DOM tree.  
+        /// Pretty bad code with the indent checks, but works for now.
+        /// </summary>
+        private static SimpleXmlElement ParseReader(XmlReader reader)
+        {
+            List<SimpleXmlElement> parent = new List<SimpleXmlElement>();
+            SimpleXmlElement current = null;
+            int indent = 0;
+
+            while (reader.Read())
+            {
+                switch (reader.NodeType)
+                {
+                    case XmlNodeType.Element:
+
+                        if (reader.Depth != indent)
+                        {
+                            // Moved to a child
+                            if (reader.Depth > indent)
+                                parent.Add(current);
+                            indent = reader.Depth;
+                        }
+
+                        current = new SimpleXmlElement(reader.Name);
+
+                        // Root element
+                        if (indent == 0 && parent.Count == 0)
+                            parent.Add(current);
+                        else
+                            parent[indent].AppendChild(current);
+
+                        if (reader.HasAttributes)
+                        {
+                            while (reader.MoveToNextAttribute())
+                                current.Attributes.Add(reader.Name, reader.Value);
+                            reader.MoveToElement(); // move back to element
+                        }
+
+                        break;
+
+                    case XmlNodeType.EndElement:
+                        current = null;
+                        break;
+
+                    // Treat CDATA and Text the same
+                    case XmlNodeType.CDATA:
+                    case XmlNodeType.Text:
+                        if (current != null)
+                            current.SetText(reader.ReadContentAsString());
+                        break;
+
+                    default:
+                        break;
+                }
+            }
+
+            return parent.Count > 0 ? parent[0] : null;
+        }
+
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/SilverlightToolbox.csproj
===================================================================
--- /trunk/sl2/SilverlightToolbox/SilverlightToolbox.csproj (revision 1)
+++ /trunk/sl2/SilverlightToolbox/SilverlightToolbox.csproj (revision 1)
@@ -0,0 +1,72 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SilverlightToolbox</RootNamespace>
+    <AssemblyName>SilverlightToolbox</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PersistentStorage.cs" />
+    <Compile Include="Playlists\Xspf\LinkEntry.cs" />
+    <Compile Include="Playlists\Xspf\MetaEntry.cs" />
+    <Compile Include="Playlists\Xspf\Playlist.cs" />
+    <Compile Include="Playlists\Xspf\Track.cs" />
+    <Compile Include="Playlists\Xspf\W3CDateTime.cs" />
+    <Compile Include="Playlists\Xspf\XmlUtil.cs" />
+    <Compile Include="Playlists\Xspf\XspfBaseObject.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Xml\SimpleXmlDocument.cs" />
+    <Compile Include="Xml\SimpleXmlElement.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/XmlUtil.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/XmlUtil.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/XmlUtil.cs (revision 1)
@@ -0,0 +1,187 @@
+/***************************************************************************
+ *  XmlUtil.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron@abock.org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+
+using System;
+using System.Xml;
+using System.Collections.Generic;
+using SilverlightToolbox.Xml;
+
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    internal static class XmlUtil
+    {
+        static string stripNamespace(string xpath)
+        {
+            // Strip off namespace. "xspf:title" becomes "title"
+            string[] tags = xpath.Split(':');
+            string tag = tags[tags.Length - 1];
+            return tag;
+        }
+
+        internal static string ReadString(SimpleXmlElement parentNode, string xpath)
+        //internal static string ReadString(XmlNode parentNode, XmlNamespaceManager xmlns, string xpath)
+        {
+            string tag = stripNamespace(xpath);
+
+            //XmlNode node = parentNode.SelectSingleNode(xpath, xmlns);
+            IList<SimpleXmlElement> nodes = parentNode.GetElementsByTagName(tag);
+            if (nodes == null || nodes.Count == 0)
+            {
+                return null;
+            }
+
+            // Get the first node.
+            SimpleXmlElement node = nodes[0];
+            if (node == null)
+            {
+                return null;
+            }
+
+            return node.Text == null ? null : node.Text.Trim();
+        }
+
+        internal static Uri ReadUri(SimpleXmlElement node, string xpath)
+        //internal static Uri ReadUri(SimpleXmlElement node, XmlNamespaceManager xmlns, Uri baseUri, string xpath)
+        {
+            string str = ReadString(node, xpath);
+            if (str == null)
+            {
+                return null;
+            }
+
+            return new Uri(str, UriKind.RelativeOrAbsolute);
+        }
+
+
+        internal static DateTime ReadDate(SimpleXmlElement node, string xpath)
+        {
+            string str = ReadString(node, xpath);
+            if (str == null)
+            {
+                return DateTime.MinValue;
+            }
+
+            W3CDateTime datetime = W3CDateTime.Parse(str);
+            return datetime.LocalTime;
+        }
+
+        internal static uint ReadUInt(SimpleXmlElement node, string xpath)
+        {
+            string str = ReadString(node, xpath);
+            if (str == null)
+            {
+                return 0;
+            }
+
+            return UInt32.Parse(str);
+        }
+
+        internal static string ReadRelPair(SimpleXmlElement node, out Uri rel)
+        {
+            //XmlAttribute attr = node.Attributes["rel"];
+            //string rel_value = attr == null || attr.Value == null ? null : attr.Value.Trim();
+            string attr = node.Attributes["rel"];
+            string rel_value = attr == null || attr == null ? null : attr.Trim();
+            string value = node.Text == null ? null : node.Text.Trim();
+
+            if (rel_value == null || value == null)
+            {
+                rel = null;
+                return null;
+            }
+
+            rel = new Uri(rel_value);
+            return value;
+        }
+
+        internal static List<MetaEntry> ReadMeta(SimpleXmlElement parentNode, string xpath)
+        {
+            List<MetaEntry> meta_collection = new List<MetaEntry>();
+
+            string tag = stripNamespace(xpath);
+
+            foreach (SimpleXmlElement node in parentNode.GetElementsByTagName(tag))
+            {
+                Uri rel;
+                string value = ReadRelPair(node, out rel);
+
+                if (value == null || rel == null)
+                {
+                    continue;
+                }
+
+                meta_collection.Add(new MetaEntry(rel, value));
+            }
+
+            return meta_collection;
+        }
+
+        internal static List<LinkEntry> ReadLinks(SimpleXmlElement parentNode, string xpath)
+        {
+            List<LinkEntry> link_collection = new List<LinkEntry>();
+
+            string tag = stripNamespace(xpath);
+
+            foreach (SimpleXmlElement node in parentNode.GetElementsByTagName(tag))
+            {
+                Uri rel;
+                string value = ReadRelPair(node, out rel);
+
+                if (value == null || rel == null)
+                {
+                    continue;
+                }
+
+                link_collection.Add(new LinkEntry(rel, new Uri(value)));
+            }
+
+            return link_collection;
+        }
+
+        internal static List<Uri> ReadUris(SimpleXmlElement parentNode, string xpath)
+        {
+            List<Uri> uri_collection = new List<Uri>();
+
+            string tag = stripNamespace(xpath);
+
+            foreach (SimpleXmlElement node in parentNode.GetElementsByTagName(tag))
+            {
+                string value = node.Text == null ? null : node.Text.Trim();
+                if (value != null)
+                {
+                    // Convert %20 -> space and such.
+                    //value = Uri.UnescapeDataString(value);
+                    uri_collection.Add(new Uri(value, UriKind.RelativeOrAbsolute));
+                }
+            }
+
+            return uri_collection;
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/LinkEntry.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/LinkEntry.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/LinkEntry.cs (revision 1)
@@ -0,0 +1,56 @@
+/***************************************************************************
+ *  LinkEntry.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron@abock.org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+ 
+using System;
+
+
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    public struct LinkEntry
+    {
+        public static readonly LinkEntry Zero;
+        
+        private Uri rel;
+        private Uri value;
+        
+        public LinkEntry(Uri rel, Uri value)
+        {
+            this.rel = rel;
+            this.value = value;
+        }
+        
+        public Uri Rel {
+            get { return rel; }
+        }
+        
+        public Uri Value {
+            get { return value; }
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/Playlist.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/Playlist.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/Playlist.cs (revision 1)
@@ -0,0 +1,153 @@
+/***************************************************************************
+ *  Playlist.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron@abock.org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+
+
+using System;
+using System.IO;
+using System.Xml;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+using SilverlightToolbox.Xml;
+
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    public class Playlist : XspfBaseObject
+    {
+        // TODO: Add attribution, extension support
+
+        //private static string XspfNamespace = "http://xspf.org/ns/0/";
+
+        private Uri location;
+        private Uri identifier;
+        private Uri license;
+        private DateTime date;
+
+        private SimpleXmlElement root;
+
+        private List<Track> tracks = new List<Track>();
+
+        public void Load(SimpleXmlDocument doc)
+        {
+            this.root = doc.Root;
+
+            //SimpleXmlElement playlist_node = root.GetElementByTagName("/xspf:playlist");
+            SimpleXmlElement playlist_node = root.GetElementByTagName("playlist");
+
+            if (playlist_node == null)
+            {
+                throw new ApplicationException("Not a valid XSPF playlist");
+            }
+
+            /*
+            XmlAttribute version_attr = playlist_node.Attributes["version"];
+            if(version_attr == null || version_attr.Value == null) {
+                throw new ApplicationException("XSPF playlist version must be specified");
+            } else {
+                try {
+                    int version = Int32.Parse(version_attr.Value);
+                    if(version < 0 || version > 1) {
+                        throw new ApplicationException("Only XSPF versions 0 and 1 are supported");
+                    }
+                } catch(FormatException) {
+                    throw new ApplicationException("Invalid XSPF Version '" + version_attr.Value + "'");
+                }
+            }
+            
+            XmlAttribute base_attr = playlist_node.Attributes["xml:base"];
+            if(base_attr != null) {
+                document_base_uri = new Uri(base_attr.Value);
+            }
+            */
+
+            LoadBase(playlist_node);
+
+            /*
+            location = XmlUtil.ReadUri(playlist_node, xmlns, ResolvedBaseUri, "xspf:location");
+            identifier = XmlUtil.ReadUri(playlist_node, xmlns, ResolvedBaseUri, "xspf:identifier");
+            license = XmlUtil.ReadUri(playlist_node, xmlns, ResolvedBaseUri, "xspf:license");
+            
+            date = XmlUtil.ReadDate(playlist_node, xmlns, "xspf:date");
+              */
+
+            SimpleXmlElement tracklist_node = root.GetElementByTagName("trackList");
+            if (tracklist_node == null)
+            {
+                throw new ApplicationException("No trackList found");
+            }
+
+            foreach (SimpleXmlElement node in tracklist_node.GetElementsByTagName("track"))
+            {
+                Track track = new Track();
+                track.Load(this, node);
+                AddTrack(track);
+            }
+        }
+
+        public void AddTrack(Track track)
+        {
+            track.Parent = this;
+            tracks.Add(track);
+        }
+
+        public Uri Location
+        {
+            get { return location; }
+            set { location = value; }
+        }
+
+        public Uri Identifier
+        {
+            get { return identifier; }
+            set { identifier = value; }
+        }
+
+        public Uri License
+        {
+            get { return license; }
+            set { license = value; }
+        }
+
+        public DateTime Date
+        {
+            get { return date; }
+            set { date = value; }
+        }
+
+        public ReadOnlyCollection<Track> Tracks
+        {
+            get { return new ReadOnlyCollection<Track>(tracks); }
+        }
+
+        public int TrackCount
+        {
+            get { return tracks.Count; }
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/XspfBaseObject.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/XspfBaseObject.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/XspfBaseObject.cs (revision 1)
@@ -0,0 +1,130 @@
+/***************************************************************************
+ *  XspfBaseObject.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron@abock.org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+
+using System;
+using System.Xml;
+using System.Xml.Schema;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+using SilverlightToolbox.Xml;
+
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    public abstract class XspfBaseObject
+    {
+        private string title;
+        private string creator;
+        private string annotation;
+
+        private Uri info;
+        private Uri image;
+
+        private List<LinkEntry> links;
+        private List<MetaEntry> meta;
+
+        protected void LoadBase(SimpleXmlElement parentNode)
+        //protected void LoadBase(XmlNode parentNode, XmlNamespaceManager xmlns)
+        {
+            title = XmlUtil.ReadString(parentNode, "xspf:title");
+            creator = XmlUtil.ReadString(parentNode, "xspf:creator");
+            annotation = XmlUtil.ReadString(parentNode, "xspf:annotation");
+
+            info = XmlUtil.ReadUri(parentNode, "xspf:info");
+            image = XmlUtil.ReadUri(parentNode, "xspf:image");
+
+            meta = XmlUtil.ReadMeta(parentNode, "xspf:meta");
+            links = XmlUtil.ReadLinks(parentNode, "xspf:link");
+        }
+
+
+        public MetaEntry FindMetaEntry(string rel)
+        {
+            return FindMetaEntry(new Uri(rel));
+        }
+
+        public MetaEntry FindMetaEntry(Uri uri)
+        {
+            if (meta == null)
+            {
+                return MetaEntry.Zero;
+            }
+
+            foreach (MetaEntry meta_entry in meta)
+            {
+                if (meta_entry.Rel == uri)
+                {
+                    return meta_entry;
+                }
+            }
+
+            return MetaEntry.Zero;
+        }
+
+        public string Title
+        {
+            get { return title; }
+            set { title = value; }
+        }
+
+        public string Creator
+        {
+            get { return creator; }
+            set { creator = value; }
+        }
+
+        public string Annotation
+        {
+            get { return annotation; }
+            set { annotation = value; }
+        }
+
+        public Uri Info
+        {
+            get { return info; }
+            set { info = value; }
+        }
+
+        public Uri Image
+        {
+            get { return image; }
+            set { image = value; }
+        }
+
+        public ReadOnlyCollection<MetaEntry> Meta
+        {
+            get { return new ReadOnlyCollection<MetaEntry>(meta); }
+        }
+
+        public ReadOnlyCollection<LinkEntry> Links
+        {
+            get { return new ReadOnlyCollection<LinkEntry>(links); }
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/Track.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/Track.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/Track.cs (revision 1)
@@ -0,0 +1,162 @@
+/***************************************************************************
+ *  Track.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron@abock.org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+ 
+using System;
+using System.Xml;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+using SilverlightToolbox.Xml;
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    public class Track : XspfBaseObject
+    {
+        // TODO: Add attribution, extension support
+            
+        private string album;
+        
+        private uint track_num;
+        private TimeSpan duration;
+    
+        private List<Uri> locations = new List<Uri>();
+        private List<Uri> identifiers = new List<Uri>();
+        
+        private Playlist parent;
+        
+        public Track()
+        {
+        }
+
+        internal void Load(Playlist playlist, SimpleXmlElement node)
+        //internal void Load(Playlist playlist, XmlNode node, XmlNamespaceManager xmlns)
+        {            
+            LoadBase(node);
+            
+            album = XmlUtil.ReadString(node, "xspf:album");
+
+            track_num = XmlUtil.ReadUInt(node, "xspf:trackNum");
+            duration = TimeSpan.FromMilliseconds(XmlUtil.ReadUInt(node, "xspf:duration"));
+            
+            locations = XmlUtil.ReadUris(node, "xspf:location");
+            identifiers = XmlUtil.ReadUris(node, "xspf:identifier");
+        }
+        
+        public Uri GetLocationAt(int position)
+        {
+            return locations[position];
+        }
+        
+        public void InsertLocation(int position, Uri uri)
+        {
+            locations.Insert(position, uri);
+        }
+        
+        public void ReplaceLocation(int position, Uri uri)
+        {
+            locations.RemoveAt(position);
+            locations.Insert(position, uri);
+        }
+        
+        public void AddLocation(Uri uri)
+        {
+            locations.Add(uri);
+        }
+        
+        public void RemoveLocation(Uri uri)
+        {
+            locations.Remove(uri);
+        }
+        
+        public void ClearLocations()
+        {
+            locations.Clear();
+        }
+        
+        public Uri GetIdentifierAt(int position)
+        {
+            return identifiers[position];
+        }
+        
+        public void InsertIdentifier(int position, Uri uri)
+        {
+            identifiers.Insert(position, uri);
+        }
+        
+        public void AddIdentifier(Uri uri)
+        {
+            identifiers.Add(uri);
+        }
+        
+        public void RemoveIdentifier(Uri uri)
+        {
+            identifiers.Remove(uri);
+        }
+        
+        public void ClearIdentifiers()
+        {
+            identifiers.Clear();
+        }
+                
+        public string Album {
+            get { return album; }
+            set { album = value; }
+        }
+        
+        public uint TrackNumber {
+            get { return track_num; }
+            set { track_num = value; }
+        }
+        
+        public TimeSpan Duration {
+            get { return duration; }
+            set { duration = value; }
+        }
+                
+        public ReadOnlyCollection<Uri> Locations {
+            get { return new ReadOnlyCollection<Uri>(locations); }
+        }
+        
+        public ReadOnlyCollection<Uri> Identifiers {
+            get { return new ReadOnlyCollection<Uri>(identifiers); }
+        }
+        
+        public int LocationCount {
+            get { return locations.Count; }
+        }
+        
+        public int IdentifierCount {
+            get { return identifiers.Count; }
+        }
+        
+        public Playlist Parent {
+            internal set { parent = value; }
+            get { return parent; }
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/MetaEntry.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/MetaEntry.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/MetaEntry.cs (revision 1)
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *  MetaEntry.cs
+ *
+ *  Copyright (C) 2006 Novell, Inc.
+ *  Written by Aaron Bockover <aaron@abock.org>
+ ****************************************************************************/
+
+/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a
+ *  copy of this software and associated documentation files (the "Software"),  
+ *  to deal in the Software without restriction, including without limitation  
+ *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
+ *  and/or sell copies of the Software, and to permit persons to whom the  
+ *  Software is furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in 
+ *  all copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
+ *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
+ *  DEALINGS IN THE SOFTWARE.
+ */
+ 
+using System;
+
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    public struct MetaEntry
+    {
+        public static readonly MetaEntry Zero;
+    
+        private Uri rel;
+        private string value;
+        
+        public MetaEntry(Uri rel, string value)
+        {
+            this.rel = rel;
+            this.value = value;
+        }
+        
+        public Uri Rel {
+            get { return rel; }
+        }
+        
+        public string Value {
+            get { return value; }
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Playlists/Xspf/W3CDateTime.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Playlists/Xspf/W3CDateTime.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Playlists/Xspf/W3CDateTime.cs (revision 1)
@@ -0,0 +1,356 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace SilverlightToolbox.Playlists.Xspf
+//namespace Banshee.Playlists.Formats.Xspf
+{
+    public struct W3CDateTime: IComparable
+    {
+        public static readonly W3CDateTime MaxValue = new W3CDateTime(DateTime.MaxValue, TimeSpan.Zero);
+        public static readonly W3CDateTime MinValue = new W3CDateTime(DateTime.MinValue, TimeSpan.Zero);
+        
+        public static TimeSpan LocalUtcOffset {
+            get { 
+                DateTime now = DateTime.Now;
+                return now - now.ToUniversalTime();
+            }
+        }
+
+        public static W3CDateTime Now {
+            get { return new W3CDateTime(DateTime.Now); }
+        }
+        
+        public static W3CDateTime UtcNow {
+            get { return new W3CDateTime(DateTime.UtcNow); }
+        }
+
+        public static W3CDateTime Today {
+            get { return new W3CDateTime(DateTime.Today); }
+        }
+        
+        private DateTime datetime;
+        private TimeSpan offset;
+        
+        public W3CDateTime(DateTime datetime, TimeSpan offset)
+        {
+            this.datetime = datetime;
+            this.offset = offset;
+        }
+        
+        public W3CDateTime(DateTime datetime) : this(datetime, LocalUtcOffset)
+        {
+        }
+        
+        public DateTime DateTime {
+            get { return datetime; }
+        }
+        
+        public DateTime LocalTime {
+            get { return UtcTime + LocalUtcOffset; }
+        }
+        
+        public TimeSpan UtcOffset {
+            get { return offset; }
+        }
+        
+        public DateTime UtcTime {
+            get { return datetime - offset; }
+        }
+        
+        public W3CDateTime Add(TimeSpan value)
+        {
+            return new W3CDateTime(datetime + value, offset);
+        }
+
+        public W3CDateTime AddDays(double value)
+        {
+            return new W3CDateTime(datetime.AddDays(value), offset);
+        }
+
+        public W3CDateTime AddHours(double value)
+        {
+            return new W3CDateTime(datetime.AddHours(value), offset);
+        }
+
+        public W3CDateTime AddMilliseconds(double value)
+        {
+            return new W3CDateTime(datetime.AddMilliseconds(value), offset);
+        }
+
+        public W3CDateTime AddMinutes(double value)
+        {
+            return new W3CDateTime(datetime.AddMinutes(value), offset);
+        }
+
+        public W3CDateTime AddMonths(int value)
+        {
+        return new W3CDateTime(datetime.AddMonths(value), offset);
+        }
+
+        public W3CDateTime AddSeconds(double value)
+        {
+            return new W3CDateTime(datetime.AddSeconds(value), offset);
+        }
+
+        public W3CDateTime AddTicks(long value)
+        {
+            return new W3CDateTime(datetime.AddTicks(value), offset);
+        }
+
+        public W3CDateTime AddYears(int value)
+        {
+            return new W3CDateTime(datetime.AddYears(value), offset);
+        }
+
+        public override bool Equals(object o)
+        {
+            if(o == null || !(o is W3CDateTime)) {
+                return false;
+            }
+            
+            return DateTime.Equals(UtcTime, ((W3CDateTime)o).UtcTime);
+        }
+
+        public static bool Equals(W3CDateTime a, W3CDateTime b)
+        {
+            return DateTime.Equals(a.UtcTime, b.UtcTime);
+        }
+
+        public override int GetHashCode()
+        {
+            return datetime.GetHashCode() ^ offset.GetHashCode();
+        }
+
+        public TimeSpan Subtract(W3CDateTime value)
+        {
+            return UtcTime.Subtract(value.UtcTime);
+        }
+
+        public W3CDateTime Subtract(TimeSpan value)
+        {
+            return new W3CDateTime(datetime.Subtract(value), offset);
+        }
+
+        public W3CDateTime ToLocalTime(TimeSpan offset)
+        {
+            return new W3CDateTime(UtcTime - offset, offset);
+        }
+
+        public W3CDateTime ToLocalTime()
+        {
+            return ToLocalTime(LocalUtcOffset);
+        }
+
+        public W3CDateTime ToUniversalTime()
+        {
+            return new W3CDateTime(UtcTime, TimeSpan.Zero);
+        }
+    
+        public string ToString(string format)
+        {
+            switch(format) {
+                case "X":
+                    return datetime.ToString(@"ddd, dd MMM yyyy HH\:mm\:ss ") + FormatOffset(offset, "");
+                case "W":
+                    return datetime.ToString(@"yyyy-\MM\-ddTHH\:mm\:ss") + FormatOffset(offset, ":");
+                default:
+                    throw new FormatException("Unknown format specified. Use X (RFC822) or W (W3C).");
+            }
+        }
+
+        public override string ToString()
+        {
+            return ToString("W");
+        }
+
+        public static int Compare(W3CDateTime a, W3CDateTime b)
+        {
+            return DateTime.Compare(a.UtcTime, b.UtcTime);
+        }
+
+        public static W3CDateTime Parse(string s)
+        {
+            const string Rfc822DateFormat = 
+                @"^((Mon|Tue|Wed|Thu|Fri|Sat|Sun), *)?(?<day>\d\d?) +" +
+                @"(?<month>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) +" +
+                @"(?<year>\d\d(\d\d)?) +" +
+                @"(?<hour>\d\d):(?<min>\d\d)(:(?<sec>\d\d))? +" +
+                @"(?<ofs>([+\-]?\d\d\d\d)|UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT)$";
+        
+            const string W3CDateFormat =
+                @"^(?<year>\d\d\d\d)" +
+                @"(-(?<month>\d\d)(-(?<day>\d\d)(T(?<hour>\d\d):" + 
+                @"(?<min>\d\d)(:(?<sec>\d\d)(?<ms>\.\d+)?)?" +
+                @"(?<ofs>(Z|[+\-]\d\d:\d\d)))?)?)?$"; 
+
+            string combined_format = String.Format(
+                @"(?<rfc822>{0})|(?<w3c>{1})", Rfc822DateFormat, W3CDateFormat);
+
+            Regex reDate = new Regex(combined_format);
+            Match m = reDate.Match(s);
+            
+            if(!m.Success) {
+                throw new FormatException("Input is not a valid W3C or RFC822 date");
+            } 
+            
+            try {
+                bool isRfc822 = m.Groups["rfc822"].Success;
+                int year = Int32.Parse(m.Groups["year"].Value);
+                
+                if(year < 1000) {
+                    year += 2000 - (year < 50 ? 0 : 1);
+                }
+
+                int month;
+                if(isRfc822) {
+                    month = ParseRfc822Month(m.Groups["month"].Value);
+                } else {
+                    month = m.Groups["month"].Success ? Int32.Parse(m.Groups["month"].Value) : 1;
+                }
+
+                int day = m.Groups["day"].Success ? Int32.Parse(m.Groups["day"].Value) : 1;
+                int hour = m.Groups["hour"].Success ? Int32.Parse(m.Groups["hour"].Value) : 0;
+                int min = m.Groups["min"].Success ? Int32.Parse(m.Groups["min"].Value) : 0;
+                int sec = m.Groups["sec"].Success ? Int32.Parse(m.Groups["sec"].Value) : 0;
+                int ms = m.Groups["ms"].Success ? (int)Math.Round(1000 * Double.Parse(m.Groups["ms"].Value)) : 0;
+
+                TimeSpan offset = TimeSpan.Zero;
+                if(m.Groups["ofs"].Success) {
+                    offset = isRfc822 
+                        ? ParseRfc822Offset(m.Groups["ofs"].Value)
+                        : ParseW3COffset(m.Groups["ofs"].Value);
+                }
+        
+        
+                return new W3CDateTime(new DateTime(year, month, day, hour, min, sec, ms), offset);
+            } catch(Exception e) {
+                throw new FormatException("Input is not a valid W3C or RFC822 date", e);
+            }
+        }
+
+        private static readonly string [] MonthNames = new string [] {
+            "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
+            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+        };
+
+        private static int ParseRfc822Month(string monthName)
+        {
+            for(int i = 0; i < 12; i++) {
+                if(monthName == MonthNames[i]) {
+                    return i + 1;
+                }
+            }
+        
+            throw new ApplicationException("Invalid month name: " + monthName);
+        }
+
+        private static TimeSpan ParseRfc822Offset(string s)
+        {
+            if(s == string.Empty) {
+                return TimeSpan.Zero;
+            }
+            
+            int hours = 0;
+            
+            switch(s) {
+                case "UT":
+                case "GMT": break;
+                case "EDT": hours = -4; break;
+                case "EST": 
+                case "CDT": hours = -5; break;
+                case "CST": 
+                case "MDT": hours = -6; break;
+                case "MST":
+                case "PDT": hours = -7; break;
+                case "PST": hours = -8; break;
+                default:
+                    if(s[0] == '+') {
+                        string sfmt = s.Substring(1, 2) + ":" + s.Substring(3, 2);
+                        return TimeSpan.Parse(sfmt);
+                    } else {
+                        return TimeSpan.Parse(s.Insert(s.Length - 2, ":"));
+                    } 
+            }
+            
+            return TimeSpan.FromHours(hours);
+        }
+
+        private static TimeSpan ParseW3COffset(string s)
+        {
+            if(s == String.Empty || s == "Z") {
+                return TimeSpan.Zero;
+            } else if(s[0] == '+') {
+                return TimeSpan.Parse(s.Substring(1));
+            } else {
+                return TimeSpan.Parse(s);
+            }
+        }
+        
+        private static string FormatOffset(TimeSpan offset, string separator)
+        {
+            string s = String.Empty;
+            
+            if(offset >= TimeSpan.Zero) {
+                s = "+";
+            }
+            
+            return s + offset.Hours.ToString("00") + separator + offset.Minutes.ToString("00");
+        }
+
+        public static bool operator ==(W3CDateTime a, W3CDateTime b)
+        {
+            return Equals(a, b);
+        }
+
+        public static bool operator >(W3CDateTime a, W3CDateTime b)
+        {
+            return Compare(a, b) > 0;
+        }
+
+        public static bool operator >=(W3CDateTime a, W3CDateTime b)
+        {
+            return Compare(a, b) >= 0;
+        }
+
+        public static bool operator !=(W3CDateTime a, W3CDateTime b)
+        {
+            return !Equals(a, b);
+        }
+
+        public static bool operator <(W3CDateTime a, W3CDateTime b)
+        {
+            return Compare(a, b) < 0;
+        }
+
+        public static bool operator <=(W3CDateTime a, W3CDateTime b)
+        {
+            return Compare(a, b) <= 0;
+        }
+
+        public static TimeSpan operator -(W3CDateTime a, W3CDateTime b)
+        {
+            return a.Subtract(b);
+        }
+
+        public static W3CDateTime operator +(W3CDateTime datetime, TimeSpan timespan)
+        {
+            return datetime.Add(timespan);
+        }
+        
+        public static W3CDateTime operator -(W3CDateTime datetime, TimeSpan timespan)
+        {
+            return datetime.Subtract(timespan);
+        }
+        
+        public int CompareTo(object o)
+        {
+            if(o == null) {
+                return 1;
+            } else if(o is W3CDateTime) {
+                return Compare(this, (W3CDateTime)o);
+            }
+            
+            throw new ArgumentException("Must be a W3CDateTime");
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/PersistentStorage.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/PersistentStorage.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/PersistentStorage.cs (revision 1)
@@ -0,0 +1,202 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Xml;
+
+namespace SilverlightToolbox
+{
+    public interface IPersistentStorage
+    {
+        void Set(String key, String value);
+        String Get(String key);
+        String Get(String key, String defaultValue);
+        void ResetSettings();
+    }
+
+
+    public abstract class AbstractPersistentStorage : IPersistentStorage
+    {
+        // Use the template pattern.
+        protected abstract Boolean OpenPersistentStore();
+        protected abstract void DeserializeSettings();
+        protected abstract void WriteSetting(String key, String value);
+        protected abstract String ReadSetting(String key);
+        protected abstract void SerializeSettings();
+        protected abstract void ClosePersistantStore();
+        protected abstract Boolean OpenNewPersistantStore();
+
+        private void Init()
+        {
+            if (OpenPersistentStore())
+            {
+                // Ok, successfully opened the store.
+                DeserializeSettings();
+            }
+            else
+            {
+                // Unable to open the store. Create new one.
+                ResetSettings();
+                Init();
+            }
+        }
+
+        public void ResetSettings()
+        {
+            // Create a new store...
+            OpenNewPersistantStore();
+            // ...and populate with default XML structure so it'll be deserializable.
+            SerializeSettings();
+            ClosePersistantStore();
+        }
+
+
+        public void Set(String key, String value)
+        {
+            Init();
+            WriteSetting(key, value);
+            SerializeSettings();
+            ClosePersistantStore();
+        }
+
+        public String Get(String key)
+        {
+            return Get(key, null);
+        }
+
+        public String Get(String key, String defaultValue)
+        {
+            Init();
+            String value = ReadSetting(key);
+            if (value == null && defaultValue != null)
+            {
+                value = defaultValue;
+            }
+
+            ClosePersistantStore();
+            return value;
+        }
+    }
+
+
+    /// <summary>
+    /// Using the Isolated Storage functionality for persistance allows a browser independant store of data.
+    /// </summary>
+    public class IsolatedStorage : AbstractPersistentStorage
+    {
+        private const String settingsFile = "persistent.xml";
+        IsolatedStorageFile isoFile;
+        IsolatedStorageFileStream isoStream;
+        Dictionary<String, String> pairs;
+
+        public IsolatedStorage()
+        {
+            isoFile = IsolatedStorageFile.GetUserStoreForApplication();
+        }
+
+        protected override Boolean OpenPersistentStore()
+        {
+            try
+            {
+                if (isoStream == null)
+                {
+                    isoStream = new IsolatedStorageFileStream(settingsFile, FileMode.Open, isoFile);
+                }
+                else
+                {
+                    isoStream.Position = 0;
+                }
+                return true;
+            }
+            catch (FileNotFoundException)
+            {
+                return false;
+            }
+        }
+
+        protected override Boolean OpenNewPersistantStore()
+        {
+            pairs = new Dictionary<string, string>();
+            isoStream = new IsolatedStorageFileStream(settingsFile, FileMode.Create, isoFile);
+            return true;
+        }
+
+        protected override void DeserializeSettings()
+        {
+            pairs = new Dictionary<string, string>();
+            try
+            {
+
+                isoStream.Position = 0;
+                using (XmlReader reader = XmlReader.Create(isoStream))
+                {
+                    if (reader.ReadToFollowing("Settings"))
+                    {
+                        while (reader.MoveToNextAttribute())
+                        {
+                            String key = reader.Name;
+                            String value = reader.Value.ToString();
+                            pairs.Add(key, value);
+                        }
+                    }
+                }
+            }
+            catch (System.IO.FileNotFoundException)
+            {
+                // this is OK - will be not found first time in
+            }
+        }
+
+        protected override void WriteSetting(string key, string value)
+        {
+            pairs.Add(key, value);
+        }
+
+        protected override string ReadSetting(string key)
+        {
+            if (pairs.ContainsKey(key))
+            {
+                return pairs[key];
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        private void writeXML(Stream ms)
+        {
+            using (XmlWriter w = XmlWriter.Create(ms))
+            {
+                w.WriteStartElement("Settings");
+                foreach (KeyValuePair<String, String> pair in pairs)
+                {
+                    w.WriteAttributeString(pair.Key, pair.Value);
+                }
+                w.WriteEndElement();
+            }
+        }
+
+        protected override void SerializeSettings()
+        {
+            using (MemoryStream ms = new MemoryStream())
+            {
+                writeXML(ms);
+                ms.Flush();
+                // convert the stream to a string
+                string xmlFragment = System.Text.Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
+            }
+
+            isoStream.Position = 0;
+            writeXML(isoStream);
+            isoStream.Flush();
+        }
+
+        protected override void ClosePersistantStore()
+        {
+            isoStream.Close();
+            isoStream.Dispose();
+            isoStream = null;
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolbox/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverlightToolbox/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverlightToolbox/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SilverlightToolbox")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SilverlightToolbox")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof.sln
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof.sln (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof.sln (revision 1)
@@ -0,0 +1,32 @@
+﻿
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverightPluginProof", "SilverightPluginProof\SilverightPluginProof.csproj", "{0561C6CB-60F8-47B7-B862-1B70053B376E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainApp", "MainApp\MainApp.csproj", "{06901F55-5B64-4439-891A-DE447E757CF7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{DC1242F1-476E-4EF0-B399-ADD4B01136A5}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{0561C6CB-60F8-47B7-B862-1B70053B376E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0561C6CB-60F8-47B7-B862-1B70053B376E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0561C6CB-60F8-47B7-B862-1B70053B376E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0561C6CB-60F8-47B7-B862-1B70053B376E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{06901F55-5B64-4439-891A-DE447E757CF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{06901F55-5B64-4439-891A-DE447E757CF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{06901F55-5B64-4439-891A-DE447E757CF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{06901F55-5B64-4439-891A-DE447E757CF7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DC1242F1-476E-4EF0-B399-ADD4B01136A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DC1242F1-476E-4EF0-B399-ADD4B01136A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DC1242F1-476E-4EF0-B399-ADD4B01136A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DC1242F1-476E-4EF0-B399-ADD4B01136A5}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
Index: /trunk/sl2/SilverightPluginProof/MainApp/MainApp.csproj.user
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/MainApp.csproj.user (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/MainApp.csproj.user (revision 1)
@@ -0,0 +1,26 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}" xmlns="">
+        <WebProjectProperties>
+          <StartPageUrl>
+          </StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverightPluginProof/MainApp/MainPage.xaml
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/MainPage.xaml (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/MainPage.xaml (revision 1)
@@ -0,0 +1,11 @@
+﻿<Canvas x:Name="parentCanvas"
+        xmlns="http://schemas.microsoft.com/client/2007" 
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
+        Loaded="Page_Loaded" 
+        x:Class="MainApp.MainPage;assembly=ClientBin/MainApp.dll"
+        Width="640"
+        Height="480"
+        Background="White"
+        >
+
+</Canvas>
Index: /trunk/sl2/SilverightPluginProof/MainApp/MainApp.csproj
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/MainApp.csproj (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/MainApp.csproj (revision 1)
@@ -0,0 +1,84 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{06901F55-5B64-4439-891A-DE447E757CF7}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MainApp</RootNamespace>
+    <AssemblyName>MainApp</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="MainControl.xaml.cs">
+      <DependentUpon>MainControl.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="MainPage.xaml.cs">
+      <DependentUpon>MainPage.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="MainControl.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <SilverlightPage Include="MainPage.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </SilverlightPage>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Common\Common.csproj">
+      <Project>{DC1242F1-476E-4EF0-B399-ADD4B01136A5}</Project>
+      <Name>Common</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverightPluginProof/MainApp/MainControl.xaml
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/MainControl.xaml (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/MainControl.xaml (revision 1)
@@ -0,0 +1,21 @@
+﻿<Canvas xmlns="http://schemas.microsoft.com/client/2007" 
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
+        Width="640"
+        Height="480"
+        Background="White"
+        x:Name="parentCanvas"
+        >
+
+  <Rectangle Width="640" Height="472" Stroke="#FF000000" Canvas.Top="8" RadiusX="25.5" RadiusY="25.5">
+    <Rectangle.Fill>
+      <LinearGradientBrush EndPoint="0.425,0.998" StartPoint="0.439,-0.002">
+        <GradientStop Color="#FF414141" Offset="0"/>
+        <GradientStop Color="#FF929292" Offset="1"/>
+        <GradientStop Color="#FFAFAEAE" Offset="0.538"/>
+      </LinearGradientBrush>
+    </Rectangle.Fill>
+  </Rectangle>
+
+  <TextBlock Foreground="Black" Text="This is MainApp.MainControl - a plugin" Canvas.Left="68" Canvas.Top="98" />
+
+</Canvas>
Index: /trunk/sl2/SilverightPluginProof/MainApp/MainPage.xaml.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/MainPage.xaml.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/MainPage.xaml.cs (revision 1)
@@ -0,0 +1,23 @@
+﻿using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+namespace MainApp
+{
+    public partial class MainPage : Canvas
+    {
+         public void Page_Loaded(object o, EventArgs e)
+        {
+            // Required to initialize variables
+            InitializeComponent();
+
+         
+        }
+    }
+}
Index: /trunk/sl2/SilverightPluginProof/MainApp/MainControl.xaml.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/MainControl.xaml.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/MainControl.xaml.cs (revision 1)
@@ -0,0 +1,44 @@
+﻿using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+
+using Common;
+
+namespace MainApp
+{
+    public class MainControl : ControlBase, IPlugin
+    {
+        public MainControl()
+        {
+            LoadXaml("MainApp.MainControl.xaml");
+        }
+
+
+        protected override void UpdateLayout()
+        {
+            // nothing to do for now
+        }
+
+        #region IPlugin Members
+
+        FrameworkElement IPlugin.RootElement
+        {
+            get { return this ; }
+        }
+
+        #endregion
+
+/*        public MainControl()
+        {
+            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("MainApp.MainControl.xaml");
+            this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
+        }
+ */
+    }
+}
Index: /trunk/sl2/SilverightPluginProof/MainApp/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/MainApp/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/MainApp/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MainApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("MainApp")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/TestPage.html
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/TestPage.html (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/TestPage.html (revision 1)
@@ -0,0 +1,21 @@
+﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<!-- saved from url=(0014)about:internet -->
+<head>
+    <title>Silverlight Project Test Page </title>
+    
+    <script type="text/javascript" src="Silverlight.js"></script>
+    <script type="text/javascript" src="TestPage.html.js"></script>
+    <style type="text/css">
+        .silverlightHost { width: 640px; height: 480px; }
+    </style>
+</head>
+
+<body>
+    <div id="SilverlightControlHost" class="silverlightHost" >
+        <script type="text/javascript">
+            createSilverlight();
+        </script>
+    </div>
+</body>
+</html>
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SilverightPluginProof.csproj.user
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SilverightPluginProof.csproj.user (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SilverightPluginProof.csproj.user (revision 1)
@@ -0,0 +1,25 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties>
+          <StartPageUrl>TestPage.Html</StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Web.config
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Web.config (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Web.config (revision 1)
@@ -0,0 +1,140 @@
+﻿<?xml version="1.0"?>
+<!-- 
+    Note: As an alternative to hand editing this file you can use the 
+    web admin tool to configure settings for your application. Use
+    the Website->Asp.Net Configuration option in Visual Studio.
+    A full list of settings and comments can be found in 
+    machine.config.comments usually located in 
+    \Windows\Microsoft.Net\Framework\v2.x\Config 
+-->
+<configuration>
+
+    <configSections>
+      <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+        <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+          <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
+          <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+            <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere" />
+            <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
+            <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
+            <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
+          </sectionGroup>
+        </sectionGroup>
+      </sectionGroup>
+    </configSections>  
+
+    <appSettings/>
+    <connectionStrings/>
+    <system.web>
+        <!-- 
+            Set compilation debug="true" to insert debugging 
+            symbols into the compiled page. Because this 
+            affects performance, set this value to true only 
+            during development.
+        -->
+        <compilation debug="false">
+          <assemblies>
+            <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+            <add assembly="System.Data.DataSetExtensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+            <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+            <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+          </assemblies>
+        </compilation>
+        <!--
+            The <authentication> section enables configuration 
+            of the security authentication mode used by 
+            ASP.NET to identify an incoming user. 
+        -->
+        <authentication mode="Windows" />
+        <!--
+            The <customErrors> section enables configuration 
+            of what to do if/when an unhandled error occurs 
+            during the execution of a request. Specifically, 
+            it enables developers to configure html error pages 
+            to be displayed in place of a error stack trace.
+
+        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
+            <error statusCode="403" redirect="NoAccess.htm" />
+            <error statusCode="404" redirect="FileNotFound.htm" />
+        </customErrors>
+        -->
+
+      <pages>
+        <controls>
+          <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+          <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        </controls>
+      </pages>
+
+      <httpHandlers>
+        <remove verb="*" path="*.asmx"/>
+        <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
+      </httpHandlers>
+      <httpModules>
+        <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+      </httpModules>
+
+    </system.web>
+    <system.codedom>
+      <compilers>
+        <compiler language="c#;cs;csharp" extension=".cs" 
+                  type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+          <providerOption name="CompilerVersion" value="v3.5"/>
+        </compiler>
+        <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" compilerOptions="/optioninfer+"
+                  type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+          <providerOption name="CompilerVersion" value="v3.5"/>
+        </compiler>
+      </compilers>
+    </system.codedom>
+    
+    <system.web.extensions>
+      <scripting>
+        <webServices>
+          <!-- 
+              Uncomment this section to enable the authentication service. Include 
+             requireSSL="true" if appropriate. 
+        
+          <authenticationService enabled="true" requireSSL = "true|false"/>
+          -->
+          <!-- 
+              Uncomment these lines to enable the profile service, and to choose the 
+              profile properties that can be retrieved and modified in ASP.NET AJAX 
+              applications.
+        
+           <profileService enabled="true"
+             readAccessProperties="propertyname1,propertyname2"
+             writeAccessProperties="propertyname1,propertyname2" />
+          -->
+          <!-- 
+            Uncomment this section to enable the role service.         
+            <roleService enabled="true"/>
+          -->
+        </webServices>
+        <!--
+        <scriptResourceHandler enableCompression="true" enableCaching="true" />
+        -->
+      </scripting>
+    </system.web.extensions>
+    <!-- 
+        The system.webServer section is required for running ASP.NET AJAX under Internet
+        Information Services 7.0.  It is not necessary for previous version of IIS.
+    -->
+    <system.webServer>
+      <validation validateIntegratedModeConfiguration="false"/>
+      <modules>
+        <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+      </modules>
+      <handlers>
+        <remove name="WebServiceHandlerFactory-Integrated"/>
+        <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
+             type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode"
+             type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
+      </handlers>
+    </system.webServer>
+
+</configuration>
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SilverightPluginProof.csproj
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SilverightPluginProof.csproj (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SilverightPluginProof.csproj (revision 1)
@@ -0,0 +1,83 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{0561C6CB-60F8-47B7-B862-1B70053B376E}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SilverightPluginProof</RootNamespace>
+    <AssemblyName>SilverightPluginProof</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="SLLoader.xaml.cs">
+      <DependentUpon>SLLoader.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <SilverlightPage Include="SLLoader.xaml">
+      <Generator>MSBuild:CompileXaml</Generator>
+    </SilverlightPage>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="TestPage.html" />
+    <None Include="TestPage.html.js">
+      <DependentUpon>TestPage.html</DependentUpon>
+    </None>
+    <None Include="Silverlight.js" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Common\Common.csproj">
+      <Project>{DC1242F1-476E-4EF0-B399-ADD4B01136A5}</Project>
+      <Name>Common</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SLLoader.xaml
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SLLoader.xaml (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SLLoader.xaml (revision 1)
@@ -0,0 +1,22 @@
+﻿<Canvas x:Name="parentCanvas"
+        xmlns="http://schemas.microsoft.com/client/2007" 
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
+        Loaded="Page_Loaded" 
+        x:Class="SilverightPluginProof.Page;assembly=ClientBin/SilverightPluginProof.dll"
+        Width="640"
+        Height="480"
+        Background="Yellow"
+        >
+
+  <TextBlock Text="SLLoader"
+             Canvas.Top="5"
+             />
+  
+  <Canvas x:Name="pluginCanvas"
+          Width="640"
+          Height="400"
+          Canvas.Top="80"
+          Background="Green"
+  />
+  
+</Canvas>
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Silverlight.js
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Silverlight.js (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Silverlight.js (revision 1)
@@ -0,0 +1,15 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Silverlight.js (1.1 Preview)	version 1.0   
+//
+//  This file is provided by Microsoft as a helper file for websites that
+//  incorporate Silverlight Objects. This file is provided under the Silverlight 
+//  SDK 1.1 license available at http://go.microsoft.com/fwlink/?linkid=94243.  
+//  You may not use or distribute this file or the code in this file except as 
+//  expressly permitted under that license.
+// 
+//  Copyright (c) 2007 Microsoft Corporation. All rights reserved.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+if(!window.Silverlight)window.Silverlight={};Silverlight._silverlightCount=0;Silverlight.ua=null;Silverlight.available=false;Silverlight.fwlinkRoot="http://go.microsoft.com/fwlink/?LinkID=";Silverlight.detectUserAgent=function(){var a=window.navigator.userAgent;Silverlight.ua={OS:"Unsupported",Browser:"Unsupported"};if(a.indexOf("Windows NT")>=0)Silverlight.ua.OS="Windows";else if(a.indexOf("PPC Mac OS X")>=0)Silverlight.ua.OS="MacPPC";else if(a.indexOf("Intel Mac OS X")>=0)Silverlight.ua.OS="MacIntel";if(Silverlight.ua.OS!="Unsupported")if(a.indexOf("MSIE")>=0){if(navigator.userAgent.indexOf("Win64")==-1)if(parseInt(a.split("MSIE")[1])>=6)Silverlight.ua.Browser="MSIE"}else if(a.indexOf("Firefox")>=0){var b=a.split("Firefox/")[1].split("."),c=parseInt(b[0]);if(c>=2)Silverlight.ua.Browser="Firefox";else{var d=parseInt(b[1]);if(c==1&&d>=5)Silverlight.ua.Browser="Firefox"}}else if(a.indexOf("Safari")>=0)Silverlight.ua.Browser="Safari"};Silverlight.detectUserAgent();Silverlight.isInstalled=function(d){var c=false,a=null;try{var b=null;if(Silverlight.ua.Browser=="MSIE")b=new ActiveXObject("AgControl.AgControl");else if(navigator.plugins["Silverlight Plug-In"]){a=document.createElement("div");document.body.appendChild(a);if(Silverlight.ua.Browser=="Safari")a.innerHTML='<embed type="application/x-silverlight" />';else a.innerHTML='<object type="application/x-silverlight"  data="data:," />';b=a.childNodes[0]}if(b.IsVersionSupported(d))c=true;b=null;Silverlight.available=true}catch(e){c=false}if(a)document.body.removeChild(a);return c};Silverlight.createObject=function(l,g,m,j,k,i,h){var b={},a=j,c=k;a.source=l;b.parentElement=g;b.id=Silverlight.HtmlAttributeEncode(m);b.width=Silverlight.HtmlAttributeEncode(a.width);b.height=Silverlight.HtmlAttributeEncode(a.height);b.ignoreBrowserVer=Boolean(a.ignoreBrowserVer);b.inplaceInstallPrompt=Boolean(a.inplaceInstallPrompt);var e=a.version.split(".");b.shortVer=e[0]+"."+e[1];b.version=a.version;a.initParams=i;a.windowless=a.isWindowless;a.maxFramerate=a.framerate;for(var d in c)if(c[d]&&d!="onLoad"&&d!="onError"){a[d]=c[d];c[d]=null}delete a.width;delete a.height;delete a.id;delete a.onLoad;delete a.onError;delete a.ignoreBrowserVer;delete a.inplaceInstallPrompt;delete a.version;delete a.isWindowless;delete a.framerate;delete a.data;delete a.src;if(Silverlight.isInstalled(b.version)){if(Silverlight._silverlightCount==0)if(window.addEventListener)window.addEventListener("onunload",Silverlight.__cleanup,false);else window.attachEvent("onunload",Silverlight.__cleanup);var f=Silverlight._silverlightCount++;a.onLoad="__slLoad"+f;a.onError="__slError"+f;window[a.onLoad]=function(a){if(c.onLoad)c.onLoad(document.getElementById(b.id),h,a)};window[a.onError]=function(a,b){if(c.onError)c.onError(a,b);else Silverlight.default_error_handler(a,b)};slPluginHTML=Silverlight.buildHTML(b,a)}else slPluginHTML=Silverlight.buildPromptHTML(b);if(b.parentElement)b.parentElement.innerHTML=slPluginHTML;else return slPluginHTML};Silverlight.supportedUserAgent=function(c){var a=Silverlight.ua,b=a.OS=="Unsupported"||a.Browser=="Unsupported"||a.OS=="Windows"&&a.Browser=="Safari"||a.OS.indexOf("Mac")>=0&&a.Browser=="IE";if(c=="1.1")return !(b||a.OS=="MacPPC");else return !b};Silverlight.buildHTML=function(c,d){var a=[],e,i,g,f,h;if(Silverlight.ua.Browser=="Safari"){a.push("<embed ");e="";i=" ";g='="';f='"';h=' type="application/x-silverlight"/>'+"<iframe style='visibility:hidden;height:0;width:0'/>"}else{a.push('<object type="application/x-silverlight" data="data:,"');e=">";i=' <param name="';g='" value="';f='" />';h="</object>"}a.push(' id="'+c.id+'" width="'+c.width+'" height="'+c.height+'" '+e);for(var b in d)if(d[b])a.push(i+Silverlight.HtmlAttributeEncode(b)+g+Silverlight.HtmlAttributeEncode(d[b])+f);a.push(h);return a.join("")};Silverlight.default_error_handler=function(e,b){var d,c=b.ErrorType;d=b.ErrorCode;var a="\nSilverlight error message     \n";a+="ErrorCode: "+d+"\n";a+="ErrorType: "+c+"       \n";a+="Message: "+b.ErrorMessage+"     \n";if(c=="ParserError"){a+="XamlFile: "+b.xamlFile+"     \n";a+="Line: "+b.lineNumber+"     \n";a+="Position: "+b.charPosition+"     \n"}else if(c=="RuntimeError"){if(b.lineNumber!=0){a+="Line: "+b.lineNumber+"     \n";a+="Position: "+b.charPosition+"     \n"}a+="MethodName: "+b.methodName+"     \n"}alert(a)};Silverlight.createObjectEx=function(b){var a=b,c=Silverlight.createObject(a.source,a.parentElement,a.id,a.properties,a.events,a.initParams,a.context);if(a.parentElement==null)return c};Silverlight.buildPromptHTML=function(f){var a=null,h=Silverlight.fwlinkRoot,c=Silverlight.ua.OS,b="92822",d,e="Get Microsoft Silverlight",m="0x409";if(f.shortVer=="1.1")f.inplaceInstallPrompt=false;if(f.inplaceInstallPrompt){var j;if(Silverlight.available){d="94376";j="94382"}else{d="92802";j="94381"}var i="93481",g="93483";if(c=="Windows"){b="92799";i="92803";g="92805"}else if(c=="MacIntel"){b="92808";i="92804";g="92806"}else if(c=="MacPPC"){b="92807";i="92815";g="92816"}var l='By clicking <b>"Get Microsoft Silverlight"</b> you accept the<br /><a title="Silverlight License Agreement" href="{2}" target="_top" style="text-decoration: underline; color: #36A6C6"><b>Silverlight license agreement</b></a>',k='Silverlight updates automatically, <a title="Silverlight Privacy Statement" href="{3}" target="_top" style="text-decoration: underline; color: #36A6C6"><b>learn more</b></a>';a='<table border="0" cellpadding="0" cellspacing="0" width="206px"><tr><td><img style="display: block; cursor: pointer; border= 0;" title="'+e+'" alt="'+e+'" onclick="javascript:Silverlight.followFWLink({0});" src="{1}" /></td></tr><tr><td style="width: 206px; margin: 0px; background: #C7C7BD; color: black; text-align: left; border-left-style: solid; border-right-style: solid; padding-left: 6px; padding-right: 6px; padding-top: 3px; padding-bottom: 2px; border-width: 2px; border-color: #c7c7bd; font-family: Verdana; font-size: 55%">'+l+'</td></tr><tr><td style="width: 206px; margin: 0px; background: #817d77; color: white; text-align: left; border-left-style: solid; border-right-style: solid; padding-left: 6px; padding-right: 6px; padding-top: 3px; padding-bottom: 2px; border-width: 2px; border-color: #c7c7bd; font-family: Verdana; font-size: 55%">'+k+'</td></tr><tr><td><img alt="" src="{4}" /></td></tr></table>';a=a.replace("{2}",h+i);a=a.replace("{3}",h+g);a=a.replace("{4}",h+j)}else{if(f.shortVer=="1.1"){b="92821";if(Silverlight.available)d="94378";else d="92810";if(c=="Windows")b="92809";else if(c=="MacIntel")b="92813"}else{if(Silverlight.available)d="94377";else d="92801";if(c=="Windows")b="92800";else if(c=="MacIntel")b="92812";else if(c=="MacPPC")b="92811"}a='<div style="display:block; width: 205px; height: 67px; background-color: #FFFFFF"><img onclick="javascript:Silverlight.followFWLink({0});" style="border:0; cursor:pointer" src="{1}" title="'+e+'" alt="'+e+'"/></div>'}a=a.replace("{0}",b);a=a.replace("{1}",h+d+"&amp;clcid="+m);return a};Silverlight.__cleanup=function(){for(var a=Silverlight._silverlightCount-1;a>=0;a--){window["__slLoad"+a]=null;window["__slError"+a]=null}if(window.removeEventListener)window.removeEventListener("unload",Silverlight.__cleanup,false);else window.detachEvent("onunload",Silverlight.__cleanup)};Silverlight.followFWLink=function(a){top.location=Silverlight.fwlinkRoot+String(a)};Silverlight.HtmlAttributeEncode=function(c){var a,b="";if(c==null)return null;for(var d=0;d<c.length;d++){a=c.charCodeAt(d);if(a>96&&a<123||a>64&&a<91||a>43&&a<58&&a!=47||a==95)b=b+String.fromCharCode(a);else b=b+"&#"+a+";"}return b}
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SLLoader.xaml.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SLLoader.xaml.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/SLLoader.xaml.cs (revision 1)
@@ -0,0 +1,63 @@
+﻿using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Reflection;
+using Common;
+
+
+namespace SilverightPluginProof
+{
+
+    public partial class Page : Canvas
+    {
+        public void Page_Loaded(object o, EventArgs e)
+        {
+            // Required to initialize variables
+            InitializeComponent();
+
+            // Load main dll
+
+            try
+            {
+                // Copy the DLL of MainApp (MainhApp.dll to the SilverlightPluginProof\ClientBin)
+
+                PluginAssembly assembly = new PluginAssembly("MainApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
+                    new Uri("MainApp.dll", UriKind.Relative));
+
+                // load the assembly into our app domain
+                Assembly pluginAssembly = Assembly.Load(assembly.AssemblyName);
+
+                Type[] types = pluginAssembly.GetTypes();
+
+                // enumerate all types in the assembly and pull out all the ones that are of type IPlugin
+                // this allows for more than one IPlugin per assembly.
+                foreach (Type type in types)
+                {
+                    if (type.GetInterface(typeof(IPlugin).FullName, false) != null)
+                    {
+                        // instantiate and load the plugin
+                        Control instance = (Control)Activator.CreateInstance(type);
+
+                        // _plugins.Add(instance);
+                        pluginCanvas.Children.Add(instance);
+                    }
+                    else
+                    {
+                        // doesn't implement the interface we want
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Debug.WriteLine(ex.ToString());
+
+            }
+        }
+    }
+}
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/TestPage.html.js
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/TestPage.html.js (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/TestPage.html.js (revision 1)
@@ -0,0 +1,26 @@
+// JScript source code
+
+//contains calls to silverlight.js, example below loads Page.xaml
+function createSilverlight()
+{
+	Silverlight.createObjectEx({
+		source: "SLLoader.xaml",
+		parentElement: document.getElementById("SilverlightControlHost"),
+		id: "SilverlightControl",
+		properties: {
+			width: "100%",
+			height: "100%",
+			version: "1.1",
+			enableHtmlAccess: "true"
+		},
+		events: {}
+	});
+	   
+	// Give the keyboard focus to the Silverlight control by default
+    document.body.onload = function() {
+      var silverlightControl = document.getElementById('SilverlightControl');
+      if (silverlightControl)
+      silverlightControl.focus();
+    }
+
+}
Index: /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/SilverightPluginProof/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SilverightPluginProof")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SilverightPluginProof")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverightPluginProof/Common/Common.csproj.user
===================================================================
--- /trunk/sl2/SilverightPluginProof/Common/Common.csproj.user (revision 1)
+++ /trunk/sl2/SilverightPluginProof/Common/Common.csproj.user (revision 1)
@@ -0,0 +1,26 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}" xmlns="">
+        <WebProjectProperties>
+          <StartPageUrl>
+          </StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverightPluginProof/Common/Common.csproj
===================================================================
--- /trunk/sl2/SilverightPluginProof/Common/Common.csproj (revision 1)
+++ /trunk/sl2/SilverightPluginProof/Common/Common.csproj (revision 1)
@@ -0,0 +1,63 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{DC1242F1-476E-4EF0-B399-ADD4B01136A5}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Common</RootNamespace>
+    <AssemblyName>Common</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Common.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverightPluginProof/Common/Common.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/Common/Common.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/Common/Common.cs (revision 1)
@@ -0,0 +1,170 @@
+﻿using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using System.Reflection;
+using System.IO;
+
+namespace Common
+{   
+    public interface IPlugin
+    {
+        FrameworkElement RootElement { get; }
+
+        // you can put in lots of other things here, like an Activate or Start method, 
+        // functions to provide access to a common in-memory data store etc.
+    }
+
+
+    public class PluginAssembly
+    {
+        private string _assemblyName;
+        private Uri _url;
+
+        public PluginAssembly()
+        {
+
+        }
+
+        public PluginAssembly(string assemblyName, Uri url)
+        {
+            _assemblyName = assemblyName;
+            _url = url;
+        }
+
+
+
+        public string AssemblyName
+        {
+            get { return _assemblyName; }
+            set { _assemblyName = value; }
+        }
+
+        public Uri Url
+        {
+            get { return _url; }
+            set { _url = value; }
+        }
+    }
+
+    public abstract class ControlBase : Control
+    {
+        private DependencyObject _rootElement;  // Set by LoadXaml. This is the starting point for all name searches
+        protected bool _layoutSuspended = false;
+
+        public ControlBase()
+        {
+            Loaded += new EventHandler(OnLoaded);
+        }
+
+        // assumes xaml file is in the same assembly
+        public ControlBase(string xamlResouceName)
+            : this()
+        {
+            LoadXaml(xamlResouceName);
+        }
+
+        public ControlBase(Assembly assembly)
+            : this()
+        {
+            Stream resourceStream = null;
+            string resourceName = this.GetType().ToString();
+
+            if (assembly == null)
+            {
+                assembly = Assembly.GetCallingAssembly();
+            }
+
+            resourceStream = assembly.GetManifestResourceStream(resourceName);
+        }
+
+
+        /// <summary>
+        /// Searches for an element by its name. Since this is a generic function,
+        /// it returns a strongly-typed reference to the found element
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        protected T FindByName<T>(string name) where T : DependencyObject
+        {
+            return _rootElement.FindName(name) as T;
+        }
+
+        /// <summary>
+        /// Set by LoadXaml. This is the starting point for all name searches.
+        /// </summary>
+        protected DependencyObject RootElement
+        {
+            get { return _rootElement; }
+            set { _rootElement = value; }
+        }
+
+        protected virtual void OnLoaded(object sender, EventArgs e)
+        {
+            UpdateLayout();
+        }
+
+        /// <summary>
+        /// Handles the InitializeFromXaml call that typically shows up 
+        /// in a usercontrol's constructor.
+        /// </summary>
+        /// <param name="xamlResourceName"></param>
+        protected void LoadXaml(string xamlResourceName)
+        {
+            try
+            {
+
+                System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream(xamlResourceName);
+
+                LoadXaml(s);
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Debug.WriteLine(ex.ToString());
+                throw;
+            }
+        }
+
+        /// <summary>
+        /// Handles the InitializeFromXaml call that typically shows up 
+        /// in a usercontrol's constructor.
+        /// </summary>
+        /// <param name="xamlResourceName"></param>
+        protected void LoadXaml(System.IO.Stream xamlStream)
+        {
+            try
+            {
+                Loaded += new EventHandler(OnLoaded);
+
+                _rootElement = this.InitializeFromXaml(new System.IO.StreamReader(xamlStream).ReadToEnd());
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Debug.WriteLine(ex.ToString());
+                throw;
+            }
+        }
+
+        public void SuspendLayout()
+        {
+            _layoutSuspended = true;
+        }
+
+        public void ResumeLayout()
+        {
+            _layoutSuspended = false;
+            UpdateLayout();
+        }
+
+        protected abstract void UpdateLayout();
+
+    }
+
+
+}
Index: /trunk/sl2/SilverightPluginProof/Common/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverightPluginProof/Common/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverightPluginProof/Common/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Common")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Common")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs.sln
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs.sln (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs.sln (revision 1)
@@ -0,0 +1,98 @@
+﻿
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightProofs", "SilverlightProofs\SilverlightProofs.csproj", "{36585F31-3C4E-4D54-83D1-9B2AF52A588A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightToolbox", "..\SilverlightToolbox\SilverlightToolbox.csproj", "{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DefaultSkin", "DefaultSkin\DefaultSkin.csproj", "{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SilverlightToolboxTests", "..\SilverlightToolboxTests\SilverlightToolboxTests.csproj", "{A7966799-428D-4131-97BF-0443F26E5B5F}"
+EndProject
+Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "SilverlightWeb", "..\SilverlightWeb\", "{AAE6B388-0FB8-4154-A054-C2C90EC28A11}"
+	ProjectSection(WebsiteProperties) = preProject
+		TargetFramework = "3.5"
+		Debug.AspNetCompiler.VirtualPath = "/SilverlightWeb"
+		Debug.AspNetCompiler.PhysicalPath = "..\SilverlightWeb\"
+		Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\SilverlightWeb\"
+		Debug.AspNetCompiler.Updateable = "true"
+		Debug.AspNetCompiler.ForceOverwrite = "true"
+		Debug.AspNetCompiler.FixedNames = "false"
+		Debug.AspNetCompiler.Debug = "True"
+		Release.AspNetCompiler.VirtualPath = "/SilverlightWeb"
+		Release.AspNetCompiler.PhysicalPath = "..\SilverlightWeb\"
+		Release.AspNetCompiler.TargetPath = "PrecompiledWeb\SilverlightWeb\"
+		Release.AspNetCompiler.Updateable = "true"
+		Release.AspNetCompiler.ForceOverwrite = "true"
+		Release.AspNetCompiler.FixedNames = "false"
+		Release.AspNetCompiler.Debug = "False"
+		VWDPort = "51167"
+		DefaultWebSiteLanguage = "Visual C#"
+		ProjOutputReferences = "{36585F31-3C4E-4D54-83D1-9B2AF52A588A}|;"
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|.NET = Debug|.NET
+		Debug|Any CPU = Debug|Any CPU
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Release|.NET = Release|.NET
+		Release|Any CPU = Release|Any CPU
+		Release|Mixed Platforms = Release|Mixed Platforms
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Debug|.NET.ActiveCfg = Debug|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Release|.NET.ActiveCfg = Release|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{36585F31-3C4E-4D54-83D1-9B2AF52A588A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Debug|.NET.ActiveCfg = Debug|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Release|.NET.ActiveCfg = Release|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Debug|.NET.ActiveCfg = Debug|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Release|.NET.ActiveCfg = Release|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Debug|.NET.ActiveCfg = Debug|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Release|.NET.ActiveCfg = Release|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{A7966799-428D-4131-97BF-0443F26E5B5F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Debug|.NET.ActiveCfg = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Debug|.NET.Build.0 = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Debug|Any CPU.ActiveCfg = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Debug|Mixed Platforms.ActiveCfg = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Debug|Mixed Platforms.Build.0 = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Release|.NET.ActiveCfg = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Release|.NET.Build.0 = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Release|Any CPU.ActiveCfg = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Release|Mixed Platforms.ActiveCfg = Debug|.NET
+		{AAE6B388-0FB8-4154-A054-C2C90EC28A11}.Release|Mixed Platforms.Build.0 = Debug|.NET
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Page.xaml.cs
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Page.xaml.cs (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Page.xaml.cs (revision 1)
@@ -0,0 +1,184 @@
+﻿using System;
+// For parsing the arguments
+using System.Collections.Generic;
+using System.Windows;
+// For the Scriptable decorator (In System.Silverlight)
+using System.Windows.Browser;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Shapes;
+using SilverlightToolbox;
+using SilverlightToolbox.Playlists.Xspf;
+using SilverlightToolbox.Xml;
+
+namespace SilverlightProofs
+{
+    // Allow external JS code to interact with scriptable methods below
+    [Scriptable]
+    public partial class Page : Canvas
+    {
+        private Rectangle theRectangle = null;
+
+        // Use a form of persistent storage to get and set user settings.
+        IPersistentStorage iso;
+
+        public void Page_Loaded(object o, EventArgs e)
+        {
+            // Required to initialize variables
+            InitializeComponent();
+
+            // Export this class for external visibility.
+            WebApplication.Current.RegisterScriptableObject("Page", this);
+
+            // Get "TheRect" from the Page.xaml page
+            theRectangle = TheRect;
+            // Add eventhandler from C# to C#
+            theRectangle.MouseLeftButtonUp += new MouseEventHandler(Make_Red);
+
+            // Use startup arguments
+            parseInitParams();
+
+            // Display the user settings from the persisent store.
+            iso = new IsolatedStorage();
+            ClickGetKeyButton(this, null);
+
+            // Start the timer loop
+            InitTimer();
+
+            // Try parsing a XSPF playlist
+            ReadXSPF();
+        }
+
+        void Make_Red(object sender, MouseEventArgs e)
+        {
+            // Color the rectangle RED
+            changeRectangleColor(Colors.Red);
+        }
+
+        void Make_Yellow(object o, EventArgs e)
+        {
+            // Color the rectangle YELLOW
+            changeRectangleColor(Colors.Yellow);
+        }
+
+        // Allow JS to bind this event.
+        [Scriptable]
+        public event EventHandler<EventArgs> ColorChangedEvent;
+
+        // Allow JS to bind this event.
+        [Scriptable]
+        public event EventHandler<EventArgs> DoNotSubscribeToThisEvent;
+
+        void changeRectangleColor(Color newColor)
+        {
+            // Color the rectangle RED
+            SolidColorBrush brush = new SolidColorBrush();
+            brush.Color = newColor;
+            theRectangle.Fill = brush;
+
+            // Send out JS event to indicate color change.
+            if (ColorChangedEvent != null)
+            {
+                EventArgs ea = new EventArgs();
+                ColorChangedEvent.Invoke(this, ea);
+            }
+
+            // Also send out another event that is supposed to have zero subscribers, to check if JS warns us.
+            if (DoNotSubscribeToThisEvent != null)
+            {
+                EventArgs ea = new EventArgs();
+                DoNotSubscribeToThisEvent.Invoke(this, ea);
+            }
+        }
+
+        [Scriptable]
+        public void SetRectangleColor(String strColor)
+        {
+            Color color;
+            switch (strColor.ToLower())
+            {
+                case "green": color = Colors.Green; break;
+                default: color = Colors.Gray; break;
+            }
+            changeRectangleColor(color);
+        }
+
+        void parseInitParams()
+        {
+            // Get the startup arguments (passed from the hosting web page).
+            IDictionary<string, string> startupArguments = WebApplication.Current.StartupArguments;
+
+            // Display all arguments in a textblock.
+            TextBlock tb = new TextBlock
+            {
+                TextWrapping = TextWrapping.Wrap
+            };
+
+            foreach (KeyValuePair<string, string> kv in startupArguments)
+            {
+                tb.Text += kv.Key + ":" + kv.Value + " ";
+            }
+            this.Children.Add(tb);
+        }
+
+        // These 3 eventhandler demonstrate the persisent storage by setting and getting a key-value pair.
+        void ClickGetKeyButton(object o, EventArgs e)
+        {
+            TheValue.Text = iso.Get("testkey", "No value...");
+        }
+        void ClickStoreKeyButton(object o, EventArgs e)
+        {
+            iso.Set("testkey", "hello");
+        }
+        void ClickResetStoreButton(object o, EventArgs e)
+        {
+            iso.ResetSettings();
+        }
+
+        Storyboard timer;
+        void InitTimer()
+        {
+            timer = (Storyboard)XamlReader.Load("<Storyboard xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Name=\"TimerClick\" Duration=\"00:00:02\" />");
+            timer.Completed += new EventHandler(timer_Completed);
+            this.Resources.Add(timer);
+            timer.Begin();
+        }
+
+        int timerCounter = 0;
+        void timer_Completed(object sender, EventArgs e)
+        {
+            timerCounter++;
+            TheTimerCounter.Text = timerCounter.ToString();
+            timer.Begin();
+        }
+
+        private void ReadXSPF()
+        {
+            string url = "Resources/xspf_full_playlist.xml";
+            Downloader downloader = new Downloader();
+            downloader.Completed += new EventHandler(downloadCompleted);
+            downloader.Open("GET", new Uri(url, UriKind.RelativeOrAbsolute));
+            downloader.Send();
+        }
+
+        void downloadCompleted(object sender, EventArgs e)
+        {
+            Downloader d = (Downloader)sender;
+
+            // Retrieve downloaded content.
+            var xmlFragment = d.ResponseText;
+
+            Playlist playlist = new Playlist();
+            SimpleXmlDocument doc = new SimpleXmlDocument(xmlFragment);
+            playlist.Load(doc);
+            FillPlaylist(playlist);
+        }
+
+        private void FillPlaylist(Playlist playlist)
+        {
+            TheValue.Text = "Loaded " + playlist.TrackCount + " items from playlist";
+        }
+    }
+}
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/SilverlightProofs.csproj.user
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/SilverlightProofs.csproj.user (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/SilverlightProofs.csproj.user (revision 1)
@@ -0,0 +1,25 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties>
+          <StartPageUrl>Playground.html</StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Web.config
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Web.config (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Web.config (revision 1)
@@ -0,0 +1,140 @@
+﻿<?xml version="1.0"?>
+<!-- 
+    Note: As an alternative to hand editing this file you can use the 
+    web admin tool to configure settings for your application. Use
+    the Website->Asp.Net Configuration option in Visual Studio.
+    A full list of settings and comments can be found in 
+    machine.config.comments usually located in 
+    \Windows\Microsoft.Net\Framework\v2.x\Config 
+-->
+<configuration>
+
+    <configSections>
+      <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+        <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+          <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
+          <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+            <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere" />
+            <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
+            <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
+            <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
+          </sectionGroup>
+        </sectionGroup>
+      </sectionGroup>
+    </configSections>  
+
+    <appSettings/>
+    <connectionStrings/>
+    <system.web>
+        <!-- 
+            Set compilation debug="true" to insert debugging 
+            symbols into the compiled page. Because this 
+            affects performance, set this value to true only 
+            during development.
+        -->
+        <compilation debug="false">
+          <assemblies>
+            <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+            <add assembly="System.Data.DataSetExtensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+            <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+            <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+          </assemblies>
+        </compilation>
+        <!--
+            The <authentication> section enables configuration 
+            of the security authentication mode used by 
+            ASP.NET to identify an incoming user. 
+        -->
+        <authentication mode="Windows" />
+        <!--
+            The <customErrors> section enables configuration 
+            of what to do if/when an unhandled error occurs 
+            during the execution of a request. Specifically, 
+            it enables developers to configure html error pages 
+            to be displayed in place of a error stack trace.
+
+        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
+            <error statusCode="403" redirect="NoAccess.htm" />
+            <error statusCode="404" redirect="FileNotFound.htm" />
+        </customErrors>
+        -->
+
+      <pages>
+        <controls>
+          <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+          <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        </controls>
+      </pages>
+
+      <httpHandlers>
+        <remove verb="*" path="*.asmx"/>
+        <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
+      </httpHandlers>
+      <httpModules>
+        <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+      </httpModules>
+
+    </system.web>
+    <system.codedom>
+      <compilers>
+        <compiler language="c#;cs;csharp" extension=".cs" 
+                  type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+          <providerOption name="CompilerVersion" value="v3.5"/>
+        </compiler>
+        <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" compilerOptions="/optioninfer+"
+                  type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+          <providerOption name="CompilerVersion" value="v3.5"/>
+        </compiler>
+      </compilers>
+    </system.codedom>
+    
+    <system.web.extensions>
+      <scripting>
+        <webServices>
+          <!-- 
+              Uncomment this section to enable the authentication service. Include 
+             requireSSL="true" if appropriate. 
+        
+          <authenticationService enabled="true" requireSSL = "true|false"/>
+          -->
+          <!-- 
+              Uncomment these lines to enable the profile service, and to choose the 
+              profile properties that can be retrieved and modified in ASP.NET AJAX 
+              applications.
+        
+           <profileService enabled="true"
+             readAccessProperties="propertyname1,propertyname2"
+             writeAccessProperties="propertyname1,propertyname2" />
+          -->
+          <!-- 
+            Uncomment this section to enable the role service.         
+            <roleService enabled="true"/>
+          -->
+        </webServices>
+        <!--
+        <scriptResourceHandler enableCompression="true" enableCaching="true" />
+        -->
+      </scripting>
+    </system.web.extensions>
+    <!-- 
+        The system.webServer section is required for running ASP.NET AJAX under Internet
+        Information Services 7.0.  It is not necessary for previous version of IIS.
+    -->
+    <system.webServer>
+      <validation validateIntegratedModeConfiguration="false"/>
+      <modules>
+        <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+      </modules>
+      <handlers>
+        <remove name="WebServiceHandlerFactory-Integrated"/>
+        <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
+             type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode"
+             type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
+      </handlers>
+    </system.webServer>
+
+</configuration>
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Playground.html.js
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Playground.html.js (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Playground.html.js (revision 1)
@@ -0,0 +1,44 @@
+// JScript source code
+
+//contains calls to silverlight.js, example below loads Page.xaml
+function createSilverlight()
+{
+    // The createObjectEx() method uses JSON syntax to pass all of the parameter values,
+    // whereas the createObject() method uses JSON for only some of the parameters, such as the properties and event handlers.
+	Silverlight.createObjectEx({
+		source: "Page.xaml",
+		parentElement: document.getElementById("SilverlightControlHost"),
+		id: "SilverlightControl",
+		properties: {
+			width: "100%",
+			height: "100%",
+			version: "1.1",
+			enableHtmlAccess: "true"
+		},
+		events: {
+		    // The OnLoad event will be invoked once after the loaded events on every element on the tree has been fired.
+		    // The OnLoad event is an approximation of "the tree has been laid out and about to be rendered."
+		    // The event is fired only the first time the content is loaded.
+		    onLoad: OnLoaded
+		},
+		// User-defined initialization values that can be passed to a Silverlight control instance as it loads.
+		initParams: "autostart=true, repeat=false",
+		// Unique identifier that can be accessed in the OnLoad event. Useful when multiple Silverlight controls may be defined in a page.
+		context: null
+	});
+	   
+	// Give the keyboard focus to the Silverlight control by default
+    document.body.onload = function() {
+      var silverlightControl = document.getElementById('SilverlightControl');
+      if (silverlightControl)
+      silverlightControl.focus();
+    }
+}
+ 
+function OnLoaded(sender, args)
+{ 
+    // This OnLoaded global event handler for the onLoad event should be used for binding other events within our silverlight control.
+    bindSilverlightEvents();
+    
+    showInitParams(sender);
+}
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/PlaygroundEvents.js
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/PlaygroundEvents.js (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/PlaygroundEvents.js (revision 1)
@@ -0,0 +1,48 @@
+﻿// EVENTS
+function colorChangedEvent(sender, args) {
+    addLog("ColorChangedEvent handled");
+}
+
+function bindSilverlightEvents()
+{
+    var control = document.getElementById("SilverlightControl");
+    control.Content.Page.ColorChangedEvent = colorChangedEvent;
+}  
+ 
+// TOOLS
+function addLog(text)
+{
+    // Add line to event history
+    var pastEvents = document.getElementById("pastEvents");     
+    var p = document.createElement("p");
+    p.innerHTML = text;    
+    pastEvents.appendChild(p);
+}
+
+function emptyTag(id)
+{
+    var element = document.getElementById(id);
+    while (element.hasChildNodes())
+    {
+        element.removeChild(element.firstChild);
+    }
+}
+
+// INIT PARAMS
+function showInitParams(sender)
+{
+    // Retrieve a reference to the silverlight control.
+    var control = document.getElementById('SilverlightControl');
+   
+    // Retrieve the InitParams value and split comma-delimited string.
+    var params = control.initParams.split(",");
+
+    // Display the parameter values.
+    var msg = "Params: ";
+    for (var i = 0; i < params.length; i++)
+    {
+        msg += "[" + params[i] + "], ";
+    }
+    
+    addLog(msg);
+}
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/SilverlightProofs.csproj
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/SilverlightProofs.csproj (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/SilverlightProofs.csproj (revision 1)
@@ -0,0 +1,90 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{36585F31-3C4E-4D54-83D1-9B2AF52A588A}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SilverlightProofs</RootNamespace>
+    <AssemblyName>SilverlightProofs</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Page.xaml.cs">
+      <DependentUpon>Page.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <SilverlightPage Include="Page.xaml">
+      <Generator>MSBuild:CompileXaml</Generator>
+    </SilverlightPage>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Silverlight.js" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Resources\xspf_full_playlist.xml" />
+    <Content Include="Playground.html" />
+    <Content Include="Playground.html.js" />
+    <Content Include="PlaygroundEvents.js" />
+    <Content Include="README.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\SilverlightToolbox\SilverlightToolbox.csproj">
+      <Project>{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}</Project>
+      <Name>SilverlightToolbox</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\DefaultSkin\DefaultSkin.csproj">
+      <Project>{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}</Project>
+      <Name>DefaultSkin</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Playground.html
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Playground.html (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Playground.html (revision 1)
@@ -0,0 +1,50 @@
+﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!-- saved from url=(0014)about:internet -->
+<head>
+    <title>SL Media Player :: Playground</title>
+    <!-- Defines JS eventhandlers for the silverlight control -->
+    <script type="text/javascript" src="PlaygroundEvents.js"></script>
+ 
+    <!-- Get the Silverlight control or a link to the installer -->
+    <script type="text/javascript" src="Silverlight.js"></script>
+
+    <!-- Initialise the silverlight control -->
+    <script type="text/javascript" src="Playground.html.js"></script>
+    
+    <style type="text/css">
+        .silverlightHost
+        {
+            width: 640px;
+            height: 480px;
+        }
+    </style>
+</head>
+<body>
+    <p>
+       <a href="http://www.microsoft.com/silverlight/downloads.aspx">Silverlight 1.1a download link. Use this one instead of the clickable image below.</a>
+    </p>
+
+    <div id="SilverlightControlHost" class="silverlightHost">
+
+        <script type="text/javascript">
+            createSilverlight();
+        </script>
+
+    </div>
+    <!-- This button allows JS to interact with scriptable managed C# -->
+    <form action="#">
+    <!-- Get the id of the silverlight control we passed info the Silverlight.createObjectEx function.
+                    The Content object contains all objects registered in the call to WebApplication.Current.RegisterScriptableObjects.
+                    The SetRectangeColor method has been made available by decorating it with the [Scriptable] attribute. -->
+    <input type="button" name="wantGreenRect" value="I prefer green" onclick="document.getElementById('SilverlightControl').Content.Page.SetRectangleColor('Green');" />
+    </form>
+    
+     <div id="pastEvents">
+         <p>First dummy event</p>
+     </div>  
+     <form action="#">
+         <input type="button" value="Reset" onclick="emptyTag('pastEvents');"/>
+     </form>
+</body>
+</html>
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Silverlight.js
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Silverlight.js (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Silverlight.js (revision 1)
@@ -0,0 +1,15 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  Silverlight.js (1.1 Preview)	version 1.0   
+//
+//  This file is provided by Microsoft as a helper file for websites that
+//  incorporate Silverlight Objects. This file is provided under the Silverlight 
+//  SDK 1.1 license available at http://go.microsoft.com/fwlink/?linkid=94243.  
+//  You may not use or distribute this file or the code in this file except as 
+//  expressly permitted under that license.
+// 
+//  Copyright (c) 2007 Microsoft Corporation. All rights reserved.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+if(!window.Silverlight)window.Silverlight={};Silverlight._silverlightCount=0;Silverlight.ua=null;Silverlight.available=false;Silverlight.fwlinkRoot="http://go.microsoft.com/fwlink/?LinkID=";Silverlight.detectUserAgent=function(){var a=window.navigator.userAgent;Silverlight.ua={OS:"Unsupported",Browser:"Unsupported"};if(a.indexOf("Windows NT")>=0)Silverlight.ua.OS="Windows";else if(a.indexOf("PPC Mac OS X")>=0)Silverlight.ua.OS="MacPPC";else if(a.indexOf("Intel Mac OS X")>=0)Silverlight.ua.OS="MacIntel";if(Silverlight.ua.OS!="Unsupported")if(a.indexOf("MSIE")>=0){if(navigator.userAgent.indexOf("Win64")==-1)if(parseInt(a.split("MSIE")[1])>=6)Silverlight.ua.Browser="MSIE"}else if(a.indexOf("Firefox")>=0){var b=a.split("Firefox/")[1].split("."),c=parseInt(b[0]);if(c>=2)Silverlight.ua.Browser="Firefox";else{var d=parseInt(b[1]);if(c==1&&d>=5)Silverlight.ua.Browser="Firefox"}}else if(a.indexOf("Safari")>=0)Silverlight.ua.Browser="Safari"};Silverlight.detectUserAgent();Silverlight.isInstalled=function(d){var c=false,a=null;try{var b=null;if(Silverlight.ua.Browser=="MSIE")b=new ActiveXObject("AgControl.AgControl");else if(navigator.plugins["Silverlight Plug-In"]){a=document.createElement("div");document.body.appendChild(a);if(Silverlight.ua.Browser=="Safari")a.innerHTML='<embed type="application/x-silverlight" />';else a.innerHTML='<object type="application/x-silverlight"  data="data:," />';b=a.childNodes[0]}if(b.IsVersionSupported(d))c=true;b=null;Silverlight.available=true}catch(e){c=false}if(a)document.body.removeChild(a);return c};Silverlight.createObject=function(l,g,m,j,k,i,h){var b={},a=j,c=k;a.source=l;b.parentElement=g;b.id=Silverlight.HtmlAttributeEncode(m);b.width=Silverlight.HtmlAttributeEncode(a.width);b.height=Silverlight.HtmlAttributeEncode(a.height);b.ignoreBrowserVer=Boolean(a.ignoreBrowserVer);b.inplaceInstallPrompt=Boolean(a.inplaceInstallPrompt);var e=a.version.split(".");b.shortVer=e[0]+"."+e[1];b.version=a.version;a.initParams=i;a.windowless=a.isWindowless;a.maxFramerate=a.framerate;for(var d in c)if(c[d]&&d!="onLoad"&&d!="onError"){a[d]=c[d];c[d]=null}delete a.width;delete a.height;delete a.id;delete a.onLoad;delete a.onError;delete a.ignoreBrowserVer;delete a.inplaceInstallPrompt;delete a.version;delete a.isWindowless;delete a.framerate;delete a.data;delete a.src;if(Silverlight.isInstalled(b.version)){if(Silverlight._silverlightCount==0)if(window.addEventListener)window.addEventListener("onunload",Silverlight.__cleanup,false);else window.attachEvent("onunload",Silverlight.__cleanup);var f=Silverlight._silverlightCount++;a.onLoad="__slLoad"+f;a.onError="__slError"+f;window[a.onLoad]=function(a){if(c.onLoad)c.onLoad(document.getElementById(b.id),h,a)};window[a.onError]=function(a,b){if(c.onError)c.onError(a,b);else Silverlight.default_error_handler(a,b)};slPluginHTML=Silverlight.buildHTML(b,a)}else slPluginHTML=Silverlight.buildPromptHTML(b);if(b.parentElement)b.parentElement.innerHTML=slPluginHTML;else return slPluginHTML};Silverlight.supportedUserAgent=function(c){var a=Silverlight.ua,b=a.OS=="Unsupported"||a.Browser=="Unsupported"||a.OS=="Windows"&&a.Browser=="Safari"||a.OS.indexOf("Mac")>=0&&a.Browser=="IE";if(c=="1.1")return !(b||a.OS=="MacPPC");else return !b};Silverlight.buildHTML=function(c,d){var a=[],e,i,g,f,h;if(Silverlight.ua.Browser=="Safari"){a.push("<embed ");e="";i=" ";g='="';f='"';h=' type="application/x-silverlight"/>'+"<iframe style='visibility:hidden;height:0;width:0'/>"}else{a.push('<object type="application/x-silverlight" data="data:,"');e=">";i=' <param name="';g='" value="';f='" />';h="</object>"}a.push(' id="'+c.id+'" width="'+c.width+'" height="'+c.height+'" '+e);for(var b in d)if(d[b])a.push(i+Silverlight.HtmlAttributeEncode(b)+g+Silverlight.HtmlAttributeEncode(d[b])+f);a.push(h);return a.join("")};Silverlight.default_error_handler=function(e,b){var d,c=b.ErrorType;d=b.ErrorCode;var a="\nSilverlight error message     \n";a+="ErrorCode: "+d+"\n";a+="ErrorType: "+c+"       \n";a+="Message: "+b.ErrorMessage+"     \n";if(c=="ParserError"){a+="XamlFile: "+b.xamlFile+"     \n";a+="Line: "+b.lineNumber+"     \n";a+="Position: "+b.charPosition+"     \n"}else if(c=="RuntimeError"){if(b.lineNumber!=0){a+="Line: "+b.lineNumber+"     \n";a+="Position: "+b.charPosition+"     \n"}a+="MethodName: "+b.methodName+"     \n"}alert(a)};Silverlight.createObjectEx=function(b){var a=b,c=Silverlight.createObject(a.source,a.parentElement,a.id,a.properties,a.events,a.initParams,a.context);if(a.parentElement==null)return c};Silverlight.buildPromptHTML=function(f){var a=null,h=Silverlight.fwlinkRoot,c=Silverlight.ua.OS,b="92822",d,e="Get Microsoft Silverlight",m="0x409";if(f.shortVer=="1.1")f.inplaceInstallPrompt=false;if(f.inplaceInstallPrompt){var j;if(Silverlight.available){d="94376";j="94382"}else{d="92802";j="94381"}var i="93481",g="93483";if(c=="Windows"){b="92799";i="92803";g="92805"}else if(c=="MacIntel"){b="92808";i="92804";g="92806"}else if(c=="MacPPC"){b="92807";i="92815";g="92816"}var l='By clicking <b>"Get Microsoft Silverlight"</b> you accept the<br /><a title="Silverlight License Agreement" href="{2}" target="_top" style="text-decoration: underline; color: #36A6C6"><b>Silverlight license agreement</b></a>',k='Silverlight updates automatically, <a title="Silverlight Privacy Statement" href="{3}" target="_top" style="text-decoration: underline; color: #36A6C6"><b>learn more</b></a>';a='<table border="0" cellpadding="0" cellspacing="0" width="206px"><tr><td><img style="display: block; cursor: pointer; border= 0;" title="'+e+'" alt="'+e+'" onclick="javascript:Silverlight.followFWLink({0});" src="{1}" /></td></tr><tr><td style="width: 206px; margin: 0px; background: #C7C7BD; color: black; text-align: left; border-left-style: solid; border-right-style: solid; padding-left: 6px; padding-right: 6px; padding-top: 3px; padding-bottom: 2px; border-width: 2px; border-color: #c7c7bd; font-family: Verdana; font-size: 55%">'+l+'</td></tr><tr><td style="width: 206px; margin: 0px; background: #817d77; color: white; text-align: left; border-left-style: solid; border-right-style: solid; padding-left: 6px; padding-right: 6px; padding-top: 3px; padding-bottom: 2px; border-width: 2px; border-color: #c7c7bd; font-family: Verdana; font-size: 55%">'+k+'</td></tr><tr><td><img alt="" src="{4}" /></td></tr></table>';a=a.replace("{2}",h+i);a=a.replace("{3}",h+g);a=a.replace("{4}",h+j)}else{if(f.shortVer=="1.1"){b="92821";if(Silverlight.available)d="94378";else d="92810";if(c=="Windows")b="92809";else if(c=="MacIntel")b="92813"}else{if(Silverlight.available)d="94377";else d="92801";if(c=="Windows")b="92800";else if(c=="MacIntel")b="92812";else if(c=="MacPPC")b="92811"}a='<div style="display:block; width: 205px; height: 67px; background-color: #FFFFFF"><img onclick="javascript:Silverlight.followFWLink({0});" style="border:0; cursor:pointer" src="{1}" title="'+e+'" alt="'+e+'"/></div>'}a=a.replace("{0}",b);a=a.replace("{1}",h+d+"&amp;clcid="+m);return a};Silverlight.__cleanup=function(){for(var a=Silverlight._silverlightCount-1;a>=0;a--){window["__slLoad"+a]=null;window["__slError"+a]=null}if(window.removeEventListener)window.removeEventListener("unload",Silverlight.__cleanup,false);else window.detachEvent("onunload",Silverlight.__cleanup)};Silverlight.followFWLink=function(a){top.location=Silverlight.fwlinkRoot+String(a)};Silverlight.HtmlAttributeEncode=function(c){var a,b="";if(c==null)return null;for(var d=0;d<c.length;d++){a=c.charCodeAt(d);if(a>96&&a<123||a>64&&a<91||a>43&&a<58&&a!=47||a==95)b=b+String.fromCharCode(a);else b=b+"&#"+a+";"}return b}
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Resources/xspf_full_playlist.xml
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Resources/xspf_full_playlist.xml (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Resources/xspf_full_playlist.xml (revision 1)
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<playlist xml:based="http://abock.org/apples/kobbler/"
+	version="1" 
+	xmlns="http://xspf.org/ns/0/">
+	<title>Playlist Title</title>
+	<creator>Aaron Bockover</creator>
+	<annotation>Comment</annotation>
+	<location>http://abock.org/</location>
+	<identifier>http://abock.org/</identifier>
+	<image>http://abock.org/</image>
+	<date>2005-01-08T17:10:47-05:00</date>
+	<license>http://abock.org/</license>
+	<link rel="http://abock.org/1">http://abock.org/1</link>
+	<link rel="http://abock.org/2">http://abock.org/2</link>
+	<meta rel="http://abock.org/fruit">Apples</meta>
+	<meta rel="http://abock.org/fruit">Oranges</meta>
+	<trackList>
+		<track xml:base="../oranges/">
+			<location>My%20Song.ogg</location>
+			<location>http://abock.org/song.ogg</location>
+			<title>Track 1</title>
+			<creator>Aaron Bockover</creator>
+			<annotation>Comment</annotation>
+			<album>Album</album>
+			<trackNum>11</trackNum>
+			<duration>5159</duration>
+			<info>http://abock.org/</info>
+			<image>http://abock.org/</image>
+		</track>
+    <track xml:base="../oranges/">
+      <location>My%20Song2.ogg</location>
+      <location>http://abock.org/song2.ogg</location>
+      <title>Track 2</title>
+      <creator>Aaron Bockover2</creator>
+      <annotation>Comment2</annotation>
+      <album>Album2</album>
+      <trackNum>112</trackNum>
+      <duration>51592</duration>
+      <info>http://abock.org/2</info>
+      <image>http://abock.org/2</image>
+    </track>
+	</trackList>
+</playlist>
+
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/README.txt
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/README.txt (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/README.txt (revision 1)
@@ -0,0 +1,78 @@
+﻿In this project, the following proofs are performed:
+====================================================
+
+[C# -> C#]
+* Clicking on the rectangle fires an event to C# code coloring the rectangle RED. The event is attached from C#
+[XAML -> C#]
+* Focussing the rectangle fires an event to C# code coloring the rectangle YELLOW. The event is attached from XAML
+
+[JS -> C#]
+* JS calls scriptable managed code in C# to color the rectangle GREEN.
+[C# -> JS]
+* Changing a color fires an event from managed CS to JS code indicating a color change.
+* Also the DoNotSubscribeToThisEvent is invoked, which should have no subscribers, to check for any JS errors.
+[XAML -> JS]
+* Probably not possible since the MouseLeave="javascript:rectangleMouseOut" isn't allowed in SL 1.1 anymore.
+  The codebehind is C#, so no direct javascript access without going through C#.
+
+[InitParms (==FlashVars) -> C#]
+* All params are displayed on canvas by parseInitParams()
+[InitParms (==FlashVars) -> JS]
+* All params are displayed in the log by showInitParams()
+
+[Persisent Storage -> C#, C# -> Persistent Storage]
+* A key-value pair is set, get, reset through an external DLL controlling the persistent store.
+
+[Custom Silverlight Controls]
+* A seperate class library (SilverlightToolbox) holds a DefaultButton Control that is included in the proofs XAML.
+  Standard controls may be found in the Silverlight SDK. They're not part of the default distribution.
+  
+[Unittesting]
+* The WatiN framework, used in SilverlightToolboxTests, supports Nunit style tests through IE for Silverlight applications.
+  If used with TestDriven.NET, it's easy to use.
+
+[Cross-Domain downloading]
+* The Downloader class allows downloading files from the same domain. However, while debugging a silverlight app, it's
+  loaded into the browser using a file:// URL. The Downloader doesn't behave well with these URLs. Therefore, a Web project
+  is added to aid the debugging process. It's almost empty and links into the SilverlightProofs project.
+  The URL while debugging now starts with http://localhost:51167 which the Download does like better.
+  Deployed on a real webserver, there's no need to the SilverlightWeb project anymore.
+
+[XML DOM]
+* Silverlight doesn't contain any classes for traversing the DOM tree. No XMLDocument as in the full .NET 2.0 framework.
+  Got a small implementation that resembles the most important functionality.
+
+[XSPF parsing]
+* The XSPF parsing functionality for playlists and tracks is working with the above DOM implementation.
+
+
+INTERESTING URLS:
+=================
+
+[SL 1.1 tutorial collections]
+http://silverlight.net/quickstarts/managed.aspx
+http://channel9.msdn.com/wiki/default.aspx/Channel9.SilverlightFAQ
+
+[InitParams]
+http://msmvps.com/blogs/luisabreu/archive/2007/06/20/getting-your-initparams-from-managed-code.aspx
+
+[Events]
+http://blogs.msdn.com/devdave/archive/2007/05/21/calling-javascript-from-c.aspx
+
+[Timer]
+Through js:
+http://dotupdate.wordpress.com/2007/08/13/timer-for-silverlight-11/
+Through xaml:
+
+[Dynamic XAML loading]
+Through cs:
+http://community.irritatedvowel.com/blogs/pete_browns_blog/archive/2007/10/12/UserControls-as-Screens-in-Silverlight-1.1.aspx
+Through js:
+http://www.silverlight.net/QuickStarts/BuildUi/SplashScreen.aspx
+
+[Cross Domain downloading]
+http://blogs.msdn.com/vasukipsarathy/archive/2007/11/06/silverlight-1-1-ajax-futures-and-web-service.aspx
+http://dedjo.blogspot.com/2007/06/cross-domain-calls-and-server-side.html
+
+Silverlight 2.0 (now known as 1.1) is to be released at Mix'08 at 5-8 march 2008
+
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Page.xaml
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Page.xaml (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Page.xaml (revision 1)
@@ -0,0 +1,65 @@
+﻿<!-- x:Class loads the "code behind" this XAML definition. -->
+<!-- x:Name is the name of the instance by which this canvas may be used in the code behind. -->
+<!-- The Page_Loaded method is called after this Canvas has been loaded by firing Loaded event. -->
+<!-- xmlns:DefaultSkin loads code from another DLL into the DefaultSkin namespace so it's usable in this XAML file. -->
+
+<Canvas
+        xmlns="http://schemas.microsoft.com/client/2007"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        x:Class="SilverlightProofs.Page;assembly=ClientBin/SilverlightProofs.dll"
+        x:Name="parentCanvas"
+        Loaded="Page_Loaded"        
+        Width="640"
+        Height="480"
+        Background="White"        
+        xmlns:DefaultSkin="clr-namespace:DefaultSkin;assembly=ClientBin/DefaultSkin.dll"
+        >
+
+  <!-- Add eventhandler from XAML to C# -->
+  <Rectangle x:Name="TheRect"
+             Width="80"
+             Height="97"
+             Fill="Blue"
+             Stroke="#FF000000"
+             Canvas.Left="50"
+             Canvas.Top="50"
+             MouseEnter="Make_Yellow"
+             />
+
+  <!-- Show a rectangle with gradient -->
+  <Rectangle Width="149" Height="288" Stroke="#FF000000" Canvas.Left="469" Canvas.Top="67" RenderTransformOrigin="0.5,0.5" RadiusX="0" RadiusY="0" Cursor="None">
+    <Rectangle.RenderTransform>
+      <TransformGroup>
+        <ScaleTransform ScaleX="1" ScaleY="1"/>
+        <SkewTransform AngleX="0" AngleY="0"/>
+        <RotateTransform Angle="0"/>
+        <TranslateTransform X="0" Y="0"/>
+      </TransformGroup>
+    </Rectangle.RenderTransform>
+    <Rectangle.Fill>
+      <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5" MappingMode="RelativeToBoundingBox" SpreadMethod="Pad">
+        <GradientStop Color="#FFFFFFFF" Offset="0"/>
+        <GradientStop Color="#FFF82C2C" Offset="1"/>
+      </LinearGradientBrush>
+    </Rectangle.Fill>
+  </Rectangle>
+  <TextBlock Width="84" Height="20" Canvas.Left="498" Canvas.Top="76" Text="Persistence" TextWrapping="Wrap"/>
+
+  <!-- This textblocks holds the Persistent storage value -->
+  <TextBlock Width="120" Height="24" Canvas.Left="474" Canvas.Top="211" Text="No value" TextWrapping="Wrap"
+		x:Name="TheValue"/>
+  
+  <!-- These three buttons demonstrate the Persistent storage functionality. -->
+  <!-- They also demonstrate the use of custom controls. In this case, a simple button. -->
+  <!-- The Click event is exported in DefaultButton in order to fire events to the code behind this XAML definition. -->
+  <DefaultSkin:DefaultButton Width="199" Height="99" Canvas.Left="520" Canvas.Top="123" Text="Store key"
+                          x:Name="StoreKeyButton" Click= "ClickStoreKeyButton"/>
+  <DefaultSkin:DefaultButton Width="199" Height="99" Canvas.Left="520" Canvas.Top="151" Text="Reset store"
+                          x:Name="ResetStoreButton" Click= "ClickResetStoreButton"/>
+  <DefaultSkin:DefaultButton Width="199" Height="99" Canvas.Left="520" Canvas.Top="179" Text="Get key"
+                             x:Name="GetKeyButton" Click= "ClickGetKeyButton"/>
+
+  <!-- This textblocks holds the timercount value -->
+  <TextBlock Width="120" Height="24" Canvas.Left="20" Canvas.Top="20" Text="No value" TextWrapping="Wrap"
+		x:Name="TheTimerCounter"/>
+</Canvas>
Index: /trunk/sl2/SilverlightProofs/SilverlightProofs/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverlightProofs/SilverlightProofs/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverlightProofs/SilverlightProofs/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SilverlightProofs")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SilverlightProofs")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultButton.xaml
===================================================================
--- /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultButton.xaml (revision 1)
+++ /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultButton.xaml (revision 1)
@@ -0,0 +1,6 @@
+﻿<Canvas xmlns="http://schemas.microsoft.com/client/2007"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        >
+  <!-- Such a simple button -->
+  <TextBlock x:Name="tb"></TextBlock>
+</Canvas>
Index: /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultButton.xaml.cs
===================================================================
--- /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultButton.xaml.cs (revision 1)
+++ /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultButton.xaml.cs (revision 1)
@@ -0,0 +1,87 @@
+﻿using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+
+namespace DefaultSkin
+{
+    public class DefaultButton : Control
+    {
+        FrameworkElement implementationRoot;
+        TextBlock tb;
+
+        public event EventHandler Click;
+
+        public DefaultButton()
+        {
+            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("DefaultSkin.DefaultButton.xaml");
+            implementationRoot = this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
+
+            tb = implementationRoot.FindName("tb") as TextBlock;
+            this.Loaded += new EventHandler(MyLabel_Loaded);
+            base.Width = implementationRoot.Width;
+            base.Height = implementationRoot.Height;
+
+            implementationRoot.MouseLeftButtonUp += new MouseEventHandler(implementationRoot_MouseLeftButtonDown);
+        }
+
+        void implementationRoot_MouseLeftButtonDown(object sender, MouseEventArgs e)
+        {
+            if (Click != null)
+            {
+                Click(this, new EventArgs());
+            }
+        }
+
+        void MyLabel_Loaded(object sender, EventArgs e)
+        {
+            UpdateLayout();
+        }
+
+        public virtual new double Width
+        {
+            get
+            {
+                return implementationRoot.Width;
+            }
+            set
+            {
+                implementationRoot.Width = value;
+                UpdateLayout();
+            }
+        }
+
+        public virtual new double Height
+        {
+            get
+            {
+                return implementationRoot.Height;
+            }
+            set
+            {
+                implementationRoot.Height = value;
+                UpdateLayout();
+            }
+        }
+
+        public String Text
+        {
+            get
+            {
+                return tb.Text;
+            }
+            set
+            {
+                tb.Text = value;
+                UpdateLayout();
+            }
+        }
+
+        protected void UpdateLayout()
+        {
+            tb.SetValue(Canvas.LeftProperty, (Width - tb.ActualWidth) / 2);
+            tb.SetValue(Canvas.TopProperty, (Height - tb.ActualHeight) / 2);
+        }
+    }
+}
Index: /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultSkin.csproj.user
===================================================================
--- /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultSkin.csproj.user (revision 1)
+++ /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultSkin.csproj.user (revision 1)
@@ -0,0 +1,26 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}" xmlns="">
+        <WebProjectProperties>
+          <StartPageUrl>
+          </StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultSkin.csproj
===================================================================
--- /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultSkin.csproj (revision 1)
+++ /trunk/sl2/SilverlightProofs/DefaultSkin/DefaultSkin.csproj (revision 1)
@@ -0,0 +1,70 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{FE83CFBE-0AE1-4017-AF1A-2E9F24C8A32E}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>DefaultSkin</RootNamespace>
+    <AssemblyName>DefaultSkin</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="DefaultButton.xaml.cs">
+      <DependentUpon>DefaultButton.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="DefaultButton.xaml">
+      <Generator>MSBuild:Compile</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightProofs/DefaultSkin/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverlightProofs/DefaultSkin/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverlightProofs/DefaultSkin/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DefaultSkin")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("DefaultSkin")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverlightToolboxTests/SilverlightToolboxTests.csproj.user
===================================================================
--- /trunk/sl2/SilverlightToolboxTests/SilverlightToolboxTests.csproj.user (revision 1)
+++ /trunk/sl2/SilverlightToolboxTests/SilverlightToolboxTests.csproj.user (revision 1)
@@ -0,0 +1,26 @@
+﻿<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}" xmlns="">
+        <WebProjectProperties>
+          <StartPageUrl>
+          </StartPageUrl>
+          <StartAction>SpecificPage</StartAction>
+          <AspNetDebugging>True</AspNetDebugging>
+          <NativeDebugging>False</NativeDebugging>
+          <SQLDebugging>False</SQLDebugging>
+          <ExternalProgram>
+          </ExternalProgram>
+          <StartExternalURL>
+          </StartExternalURL>
+          <StartCmdLineArguments>
+          </StartCmdLineArguments>
+          <StartWorkingDirectory>
+          </StartWorkingDirectory>
+          <EnableENC>False</EnableENC>
+          <ShowWebRefOnDebugPrompt>True</ShowWebRefOnDebugPrompt>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightToolboxTests/SilverlightToolboxTests.csproj
===================================================================
--- /trunk/sl2/SilverlightToolboxTests/SilverlightToolboxTests.csproj (revision 1)
+++ /trunk/sl2/SilverlightToolboxTests/SilverlightToolboxTests.csproj (revision 1)
@@ -0,0 +1,74 @@
+﻿<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.20706</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{A7966799-428D-4131-97BF-0443F26E5B5F}</ProjectGuid>
+    <ProjectTypeGuids>{A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SilverlightToolboxTests</RootNamespace>
+    <AssemblyName>SilverlightToolboxTests</AssemblyName>
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>ClientBin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <NoStdLib>true</NoStdLib>
+    <NoConfig>true</NoConfig>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="agclr" />
+    <Reference Include="mscorlib" />
+    <Reference Include="nunit.framework, Version=2.4.3.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL" />
+    <Reference Include="system" />
+    <Reference Include="System.Core" />
+    <Reference Include="system.Xml.core" />
+    <Reference Include="system.silverlight" />
+    <Reference Include="WatiN.Core, Version=1.2.1.4000, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\Program Files (x86)\WatiN\1.2.1-net-2.0\bin\WatiN.Core.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="PersistentStorageTests.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SilverlightToolbox\SilverlightToolbox.csproj">
+      <Project>{71E7EF78-B65F-4D8E-B745-55E4CA4EA19F}</Project>
+      <Name>SilverlightToolbox</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v9.0\Silverlight\Microsoft.Silverlight.Csharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
+        <WebProjectProperties />
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+</Project>
Index: /trunk/sl2/SilverlightToolboxTests/PersistentStorageTests.cs
===================================================================
--- /trunk/sl2/SilverlightToolboxTests/PersistentStorageTests.cs (revision 1)
+++ /trunk/sl2/SilverlightToolboxTests/PersistentStorageTests.cs (revision 1)
@@ -0,0 +1,41 @@
+﻿using NUnit.Framework;
+using WatiN.Core;
+using SilverlightToolbox;
+
+namespace SilverlightToolboxTests
+{
+    public abstract class TestBase
+    {
+        protected IE ie = null;
+
+        [TestFixtureSetUpAttribute]
+        public void SetUp()
+        {
+            ie = new IE();
+            ie.GoTo("file:///C:/trunk/silverlight/SilverlightProofs/SilverlightProofs/Playground.html");
+        }
+
+        [TestFixtureTearDownAttribute]
+        public void TearDown()
+        {
+            ie.Close();
+        }
+    }
+
+
+    /// <summary>
+    /// WARNING: These tests won't run correct under 64bit Vista. Silverlight 1.1a September is 32bit only.
+    /// </summary>
+    [TestFixture]
+    public class PersistentStorageTests : TestBase
+    {
+        [Test]
+        public void Testing()
+        {
+            IPersistentStorage iso = new IsolatedStorage();
+            iso.ResetSettings();
+            iso.Set("beer", "yes please");
+            Assert.AreEqual("yes please", iso.Get("beer"));
+        }
+    }
+}
Index: /trunk/sl2/SilverlightToolboxTests/Properties/AssemblyInfo.cs
===================================================================
--- /trunk/sl2/SilverlightToolboxTests/Properties/AssemblyInfo.cs (revision 1)
+++ /trunk/sl2/SilverlightToolboxTests/Properties/AssemblyInfo.cs (revision 1)
@@ -0,0 +1,35 @@
+﻿using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SilverlightToolboxTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("SilverlightToolboxTests")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3d5900ae-111a-45be-96b3-d9e4606ca793")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
Index: /trunk/sl2/SilverlightWeb/web.config
===================================================================
--- /trunk/sl2/SilverlightWeb/web.config (revision 1)
+++ /trunk/sl2/SilverlightWeb/web.config (revision 1)
@@ -0,0 +1,129 @@
+﻿<?xml version="1.0"?>
+<!-- 
+    Note: As an alternative to hand editing this file you can use the 
+    web admin tool to configure settings for your application. Use
+    the Website->Asp.Net Configuration option in Visual Studio.
+    A full list of settings and comments can be found in 
+    machine.config.comments usually located in 
+    \Windows\Microsoft.Net\Framework\v2.x\Config 
+-->
+<configuration>
+	<configSections>
+		<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+			<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+				<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
+				<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
+					<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
+					<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
+					<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
+					<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
+				</sectionGroup>
+			</sectionGroup>
+		</sectionGroup>
+	</configSections>
+	<appSettings/>
+	<connectionStrings/>
+	<system.web>
+		<!-- 
+            Set compilation debug="true" to insert debugging 
+            symbols into the compiled page. Because this 
+            affects performance, set this value to true only 
+            during development.
+        -->
+		<compilation debug="true">
+			<assemblies>
+				<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+				<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+        <!-- 			<add assembly="System.Data.DataSetExtensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/> -->
+				<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
+			</assemblies>
+		</compilation>
+		<!--
+            The <authentication> section enables configuration 
+            of the security authentication mode used by 
+            ASP.NET to identify an incoming user. 
+        -->
+		<authentication mode="Windows"/>
+		<!--
+            The <customErrors> section enables configuration 
+            of what to do if/when an unhandled error occurs 
+            during the execution of a request. Specifically, 
+            it enables developers to configure html error pages 
+            to be displayed in place of a error stack trace.
+
+        <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
+            <error statusCode="403" redirect="NoAccess.htm" />
+            <error statusCode="404" redirect="FileNotFound.htm" />
+        </customErrors>
+        -->
+		<pages>
+			<controls>
+				<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+				<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+			</controls>
+		</pages>
+		<httpHandlers>
+			<remove verb="*" path="*.asmx"/>
+			<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+			<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+			<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
+		</httpHandlers>
+		<httpModules>
+			<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+		</httpModules>
+	</system.web>
+	<system.codedom>
+		<compilers>
+			<compiler language="c#;cs;csharp" extension=".cs" compilerOptions="/warnaserror-" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+				<providerOption name="CompilerVersion" value="v3.5"/>
+			</compiler>
+			<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" compilerOptions="/optioninfer+" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+				<providerOption name="CompilerVersion" value="v3.5"/>
+			</compiler>
+		</compilers>
+	</system.codedom>
+	<system.web.extensions>
+		<scripting>
+			<webServices>
+				<!-- 
+              Uncomment this section to enable the authentication service. Include 
+             requireSSL="true" if appropriate. 
+        
+          <authenticationService enabled="true" requireSSL = "true|false"/>
+          -->
+				<!-- 
+              Uncomment these lines to enable the profile service, and to choose the 
+              profile properties that can be retrieved and modified in ASP.NET AJAX 
+              applications.
+        
+           <profileService enabled="true"
+             readAccessProperties="propertyname1,propertyname2"
+             writeAccessProperties="propertyname1,propertyname2" />
+          -->
+				<!-- 
+            Uncomment this section to enable the role service.         
+            <roleService enabled="true"/>
+          -->
+			</webServices>
+			<!--
+        <scriptResourceHandler enableCompression="true" enableCaching="true" />
+        -->
+		</scripting>
+	</system.web.extensions>
+	<!-- 
+        The system.webServer section is required for running ASP.NET AJAX under Internet
+        Information Services 7.0.  It is not necessary for previous version of IIS.
+    -->
+	<system.webServer>
+		<validation validateIntegratedModeConfiguration="false"/>
+		<modules>
+			<add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+		</modules>
+		<handlers>
+			<remove name="WebServiceHandlerFactory-Integrated"/>
+			<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+			<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+			<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
+		</handlers>
+	</system.webServer>
+</configuration>
Index: /trunk/sl2/README.txt
===================================================================
--- /trunk/sl2/README.txt (revision 1)
+++ /trunk/sl2/README.txt (revision 1)
@@ -0,0 +1,1 @@
+Start in /SilverlightProofs/SilverlightProofs/README.txt
Index: /trunk/as2/com/jeroenwijering/players/YoutubeModel.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/YoutubeModel.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/YoutubeModel.as (revision 1)
@@ -0,0 +1,203 @@
+﻿/**
+* Youtube model class of the players MCV pattern.
+* Integrates Youtube playback component.
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.YoutubeModel extends AbstractModel {
+
+
+	/** Array with extensions used by this model. **/
+	private var mediatypes:Array = new Array("youtube");
+	/** Clip the YouTube blob is loaded into **/
+	private var ytplayer:MovieClip;
+	/** Location of the YouTube blob. **/
+	private var url:String = "http://gdata.youtube.com/apiplayer";
+	/** Developer key for the YouTube player **/
+	private var key:String = "AI39si4CHS3-oQa0cHIhANstFbCLE71-qK6CB3mNe0lEx2h1mwXsOz6n1fkPo0yTKpZgYH4jsLgSX1Qg4jXNrYhJYKfMQiPlzw";
+	/** Reference to the loader **/
+	private var loader:MovieClipLoader;
+	/** ID of the current clip to play **/
+	private var currentURL:String;
+	/** interval ID of the buffer update function **/
+	private var loadedInterval:Number;
+	/** current percentage of the video that's loaded **/
+	private var currentLoaded:Number = 0;
+	/** interval ID of the position update function **/
+	private var positionInterval:Number;
+	/** current position of the video that is playing **/
+	private var currentPosition:Number;
+	/** Current volume **/
+	private var currentVolume:Number;
+	/** static referer to the model **/
+	private static var instance;
+	/** pause-seeking **/
+	private var pauseseek:Boolean;
+	/** has the update already been sent? **/
+	private var updateSent:Boolean;
+
+
+	/** Constructor **/
+	function YoutubeModel(vws:Array,ctr:AbstractController,cfg:Object,fed:Object,fcl:MovieClip) {
+		super(vws,ctr,cfg,fed);
+		ytplayer = fcl;
+		System.security.allowDomain("gdata.youtube.com");
+		System.security.allowInsecureDomain("gdata.youtube.com");
+		YoutubeModel.instance = this;
+	};
+
+	/** wait for the player to load with a loop, then remove the loop. **/
+	public function onLoadInit() {
+		var ref = this;
+		ytplayer.onEnterFrame = function() {
+			if (this.isPlayerLoaded()) {
+				ref.pauseseek = false;
+				this.addEventListener("onStateChange",ref.onPlayerStateChange);
+				this.addEventListener("onError",ref.onError);
+				ref.onPlayerStateChange(this.getPlayerState());
+				ref.setStart(ref.feeder.feed[ref.currentItem]['duration']);
+				delete this.onEnterFrame;
+				this.setSize(ref.config['displaywidth'],ref.config['displayheight']);
+			}
+		}
+	};
+
+
+	/** Start a specific video **/
+	private function setStart(pos:Number) {
+		clearInterval(positionInterval);
+		clearInterval(loadedInterval);
+		if(feeder.feed[currentItem]["file"] != currentURL) {
+			if(!loader) {
+				loader = new MovieClipLoader();
+				loader.addListener(this);
+				loader.loadClip(url+'?key='+key,ytplayer);
+			} else {
+				currentURL = feeder.feed[currentItem]["file"];
+				ytplayer.loadVideoById(getID(currentURL),pos);
+				ytplayer.setVolume(config['volume']);
+				sendUpdate("load",0);
+				sendUpdate("size",320,240);
+			}
+		} else if(!isNaN(pos)) {
+			ytplayer.seekTo(pos,true);
+		} else {
+			ytplayer.playVideo();
+		}
+		positionInterval = setInterval(this,"updatePosition",100);
+		ytplayer._visible = true;
+		trace('true!!');
+	};
+
+
+	/** xtract the current ID from a youtube URL **/
+	private function getID(url:String):String {
+		var arr = url.split('?');
+		for (var i in arr) {
+			if(arr[i].substr(0,2) == 'v=') {
+				return arr[i].substr(2);
+			}
+		}
+		return '';
+	};
+
+
+	/** Listens for the player's onStateChange event **/
+	public function onPlayerStateChange(stt:Number) {
+		var ref = YoutubeModel.instance;
+		if(ref.currentURL == undefined) { return; }
+		switch(Number(stt)) {
+			case 0:
+				if(!ref.updateSent) { 
+					ref.sendUpdate("state",3);
+					ref.sendCompleteEvent();
+					ref.sendUpdate("time",0,ref.feeder.feed[ref.currentItem]['duration']);
+				}
+				break;
+			case 1:
+				delete ref.updateSent;
+				if(ref.pauseseek == true) {
+					ref.pauseseek = false;
+					ref.updatePosition();
+					ref.ytplayer.pauseVideo();
+				} else {
+					ref.sendUpdate("load",100);
+					ref.sendUpdate("state",2);
+				}
+				break;
+			case 3:
+				ref.sendUpdate("state",1);
+				break;
+			default:
+				ref.sendUpdate("state",0);
+				break;
+		}
+	};
+
+
+	/** Error received, let's move to the next video **/
+	public function onError() {
+		var ref = YoutubeModel.instance;
+		ref.sendUpdate("state",3);
+		ref.sendCompleteEvent();
+		ref.sendUpdate("time",0,0);
+	};
+
+
+	/** Read and broadcast the current position of the song **/
+	private function updatePosition() {
+		var pos = ytplayer.getCurrentTime();
+		var dur = ytplayer.getDuration();
+		if(isNaN(dur)) {
+			pos = 0; 
+			dur = feeder.feed[currentItem]['duration'];
+		} else {
+			feeder.feed[currentItem]['duration'] = dur;
+		}
+		sendUpdate("time",pos,dur-pos);
+		currentPosition = pos;
+	};
+
+
+	/** Pause the video that's currently playing. **/
+	private function setPause(pos:Number) {
+		clearInterval(positionInterval);
+		ytplayer.pauseVideo();
+		if(pos > 0) {
+			pauseseek = true;
+			ytplayer.seekTo(pos,true);
+			updatePosition();
+		}
+		sendUpdate("state",0);
+	};
+
+
+	/** Stop video and clear data. **/
+	private function setStop(pos:Number) {
+		updateSent = true;
+		delete currentURL;
+		ytplayer.stopVideo();
+		ytplayer._visible = false;
+		clearInterval(loadedInterval);
+		clearInterval(positionInterval);
+	};
+
+
+	/** Set volume of the sound object. **/
+	private function setVolume(vol:Number) {
+		super.setVolume(vol);
+		currentVolume = vol;
+		ytplayer.setVolume(vol);
+	};
+
+
+
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/AbstractPlayer.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/AbstractPlayer.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/AbstractPlayer.as (revision 1)
@@ -0,0 +1,98 @@
+﻿/**
+* Abstract player class, extended by all other players.
+* Class loads config and file objects and sets up MCV triangle.
+*
+* @author	Jeroen Wijering
+* @version	1.9
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.feeds.*;
+import com.jeroenwijering.utils.ConfigManager;
+
+
+class com.jeroenwijering.players.AbstractPlayer implements FeedListener {
+
+
+	/** Object with all config values **/
+	public var config:Object;
+	/** Object with all playlist items **/
+	public var feeder:FeedManager;
+	/** reference to the controller **/
+	public var controller:AbstractController;
+	/** reference to config management object **/
+	private var manager:ConfigManager;
+
+
+	/** Player application startup. **/
+	public function AbstractPlayer(tgt:MovieClip) {
+		var ref = this;
+		config["clip"] = tgt;
+		manager = new ConfigManager(true);
+		manager.onComplete = function() { ref.fillConfig(); };
+		manager.loadConfig(config);
+	};
+
+
+	/** Complete config with some default values **/
+	private function fillConfig() {
+		if(config['shownavigation'] == 'false') {
+			config['controlbar'] = 0;
+		}
+		if (config["searchbar"] == 'true' && config['height'] > 50) {
+			config["searchbar"] = 28;
+		} else {
+			config["searchbar"] = 0;
+		}
+		if (config["displayheight"] == undefined) {
+			config["displayheight"] = config["height"] - config['controlbar'] - config["searchbar"];
+		} else if(config["displayheight"] >= config["height"] - config["searchbar"]) {
+			config["displayheight"] = config["height"] - config["searchbar"];
+		} else {
+			config["displayheight"] = Number(config["displayheight"]);
+		}
+		if (config["displaywidth"] == undefined) {
+			config["displaywidth"] = config["width"];
+		} else {
+			config["displaywidth"] = Number(config["displaywidth"]);
+		}
+		config["bwstreams"] == undefined ? loadFile(): checkStream();
+	};
+
+
+
+	/** Placeholder function for bandwidth checking **/
+	private function checkStream() {};
+
+
+	/** Load the file or playlist **/
+	private function loadFile(str:String) {
+		feeder = new FeedManager(true,config["enablejs"],config['prefix'],str);
+		feeder.addListener(this);
+		feeder.loadFile(config);
+	};
+
+
+	/** Invoked by the feedmanager **/
+	public function onFeedUpdate(typ:String) {
+		if(controller == undefined) {
+			config["clip"]._visible = true;
+			config["clip"]._parent.activity._visible = false;
+			setupMCV();
+		}
+	};
+
+
+	/** Setup all necessary MCV blocks. **/
+	private function setupMCV() {
+		controller = new AbstractController(config,feeder);
+		var asv = new AbstractView(controller,config,feeder);
+		var vws:Array = new Array(asv);
+		var asm = new AbstractModel(vws,controller,config,feeder);
+		var mds:Array = new Array(asm);
+		controller.startMCV(mds);
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/MP3Model.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/MP3Model.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/MP3Model.as (revision 1)
@@ -0,0 +1,154 @@
+﻿/**
+* MP3 model class of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.4
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.MP3Model extends AbstractModel { 
+
+
+	/** array with extensions used by this model **/
+	private var mediatypes:Array = new Array("mp3","rbs");
+	/** Sound instance **/
+	private var soundObject:Sound;
+	/** MovieClip to apply the sound object to **/
+	private var soundClip:MovieClip;
+	/** interval ID of the buffer update function **/
+	private var loadedInterval:Number;
+	/** currently loaded percentage **/
+	private var currentLoaded:Number = 0;
+	/** interval ID of the position update function **/
+	private var positionInterval:Number;
+	/** current state of the sound that is playing **/
+	private var currentState:Number;
+	/** Current volume **/
+	private var currentVolume:Number;
+
+
+	/** Constructor **/
+	function MP3Model(vws:Array,ctr:AbstractController,
+		cfg:Object,fed:Object,scl:MovieClip) {
+		super(vws,ctr,cfg,fed);
+		soundClip = scl;
+	};
+
+
+	/** Start a specific sound **/
+	private function setStart(pos:Number) {
+		if(pos < 1 ) { 
+			pos = 0; 
+		} else if (pos > feeder.feed[currentItem]["duration"] - 1) { 
+			pos = feeder.feed[currentItem]["duration"] - 1;
+		}
+		clearInterval(positionInterval);
+		if(feeder.feed[currentItem]["file"] != currentURL) {
+			var ref = this;
+			currentURL = feeder.feed[currentItem]["file"];
+			soundObject = new Sound(soundClip);
+			soundObject.onSoundComplete = function() {
+				ref.currentState = 3;
+				ref.sendUpdate("state",3);
+				ref.sendCompleteEvent();
+			};
+			soundObject.onLoad = function(scs:Boolean) {
+				if(scs == false) {
+					ref.currentState = 3;
+					ref.sendUpdate("state",3);
+					ref.sendCompleteEvent();
+				}
+			};
+			soundObject.loadSound(currentURL,true);
+			soundObject.setVolume(currentVolume);
+			sendUpdate("load",0);
+			loadedInterval = setInterval(this,"updateLoaded",100);
+		}
+		if(pos != undefined) { 
+			currentPosition = pos;
+			if(pos == 0) { sendUpdate("time",0,feeder.feed[currentItem]["duration"]); }
+		}
+		soundObject.start(currentPosition);
+		updatePosition();
+		sendUpdate("size",0,0);
+		positionInterval = setInterval(this,"updatePosition",100);
+	};
+
+
+	/** Read and broadcast the amount of the mp3 that's currently loaded **/
+	private function updateLoaded() {
+		var pct:Number = Math.round(soundObject.getBytesLoaded() / 
+			soundObject.getBytesTotal()*100);
+		if(isNaN(pct)) { 
+			currentLoaded = 0; 
+			sendUpdate("load",0);
+		} else if (pct != currentLoaded) {
+			sendUpdate("load",pct); 
+			currentLoaded = pct;
+		} else if(pct >= 100) { 
+			clearInterval(loadedInterval);
+			currentLoaded = 100;
+			sendUpdate("load",100);
+		}
+	};
+
+
+	/** Read and broadcast the current position of the song **/
+	private function updatePosition() {
+		var pos = soundObject.position/1000;
+		feeder.feed[currentItem]["duration"] = soundObject.duration/(10*currentLoaded);
+		if(pos == currentPosition && currentState != 1) {
+			currentState = 1;
+			sendUpdate("state",1);
+		} else if (pos != currentPosition && currentState != 2) { 	
+			currentState = 2;
+			sendUpdate("state",2);
+		}
+		if (pos != currentPosition) {
+			currentPosition = pos;
+			sendUpdate("time",currentPosition,feeder.feed[currentItem]["duration"]-currentPosition);
+		}
+	};
+
+
+	/** Pause the sound that's currently playing. **/
+	private function setPause(pos:Number) {
+		if(pos < 1) { 
+			pos = 0; 
+		} else if (pos > feeder.feed[currentItem]["duration"] - 1) { 
+			pos = feeder.feed[currentItem]["duration"] - 1; 
+		}
+		soundObject.stop();
+		clearInterval(positionInterval);
+		currentState = 0;
+		sendUpdate("state",0);
+		if(pos != undefined) {
+			currentPosition = pos;
+			sendUpdate("time",currentPosition,feeder.feed[currentItem]["duration"]-currentPosition);
+		}
+	};
+
+
+	/** stop and unload the sound **/
+	private function setStop() {
+		soundObject.stop();
+		clearInterval(positionInterval);
+		clearInterval(loadedInterval);
+		delete currentURL;
+		delete soundObject;
+		currentLoaded = 0;
+	};
+
+
+	/** Set volume of the sound object. **/
+	private function setVolume(vol:Number) {
+		super.setVolume(vol);
+		currentVolume = vol;
+		soundObject.setVolume(vol);
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/RecommendationsView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/RecommendationsView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/RecommendationsView.as (revision 1)
@@ -0,0 +1,204 @@
+/**
+* Thumbnailbar with recommended videos.
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.utils.ImageLoader;
+import com.jeroenwijering.utils.Animations;
+import com.jeroenwijering.utils.XMLParser;
+import com.jeroenwijering.players.AbstractController;
+import com.jeroenwijering.players.AbstractView;
+
+
+class com.jeroenwijering.players.RecommendationsView extends AbstractView { 
+
+
+	/** reference to config Array **/
+	private var config:Object;
+	/** reference to feed Array **/
+	private var feeder:Object;
+	/** Reference to the movieclip **/
+	private var clip:MovieClip;
+	/** XML parser reference **/
+	private var parser:XMLParser;
+	/** Recommendations array **/
+	private var recommendations:Array;
+	/** Current show offset **/
+	private var offset:Number = 0;
+	/** Number of thumbs maximum on screen **/
+	private var maximum:Number;
+
+
+
+	/** Constructor **/
+	function RecommendationsView(ctr:AbstractController,
+		cfg:Object,fed:Object) {
+		config = cfg;
+		feeder = fed;
+		clip =  config["clip"].recommendations;
+		var ref = this;
+		parser = new XMLParser();
+		parser.onComplete = function() {
+			ref.loadRecommendations(this.output);
+		};
+		Stage.addListener(this);
+		setButtons();
+	};
+
+
+	/** Set the colors, clicks and dimensions of the buttons. **/
+	private function setButtons() {
+		var ref = this;
+		maximum = Math.floor((config['displaywidth']-44)/70);
+		clip._visible = false;
+		clip.txt._x = 10;
+		clip.txt._width = config['displaywidth'] -20;
+		clip.txt.textColor = config['backcolor'];
+		clip.prv._x = config['displaywidth']/2 - maximum*35;
+		clip.nxt._x = config['displaywidth']/2 + maximum*35;
+		clip.prv.col = new Color(clip.prv);
+		clip.prv.col.setRGB(config['backcolor']);
+		clip.prv.onRelease = function() {
+			this.col.setRGB(ref.config['backcolor']);
+			ref.showRecommendations(ref.offset - ref.maximum);
+		};
+		clip.prv._visible = false;
+		clip.nxt.col = new Color(clip.nxt);
+		clip.nxt.col.setRGB(config['backcolor']);
+		clip.nxt.onRelease = function() {
+			this.col.setRGB(ref.config['backcolor']);
+			ref.showRecommendations(ref.offset + ref.maximum);
+		};
+		clip.nxt._visible = false;
+		clip.itm._visible = false;
+		for(var i=0; i<maximum; i++) {
+			clip.itm.duplicateMovieClip('itm'+i,i);
+			clip['itm'+i]._x = clip.prv._x+i*70 + 5;
+			clip['itm'+i].ldr=new ImageLoader(clip['itm'+i].img,"true",60,45);
+			clip['itm'+i].ldr.onLoadFinished = function() {
+				Animations.fadeIn(this.targetClip._parent);
+			};
+			clip['itm'+i].img.setMask(clip['itm'+i].msk);
+			clip['itm'+i].cl1 = new Color(clip['itm'+i].bdr);
+			clip['itm'+i].cl1.setRGB(config['frontcolor']);
+			clip['itm'+i].cl2 = new Color(clip['itm'+i].icn);
+			clip['itm'+i].cl2.setRGB(config['backcolor']);
+			clip['itm'+i].icn._visible = false;
+			clip['itm'+i].onRollOver = function() {
+				this.cl1.setRGB(ref.config['backcolor']);
+				this.icn._visible = true;
+				ref.setTitle(this.num);
+			};
+			clip['itm'+i].onRollOut = function() {
+				this.cl1.setRGB(ref.config['frontcolor']);
+				this.icn._visible = false;
+				ref.clearTitle();
+			};
+			clip['itm'+i].onRelease = function() {
+				ref.getLink(this.num);
+			};
+			clip['itm'+i]._visible = false;
+			clip['itm'+i]._alpha = 0;
+		}
+	};
+
+
+	/** Load the recommendations from XML **/
+	private function loadRecommendations(rcm:Object) {
+		recommendations = new Array();
+		for (var i=0; i<rcm['childs'].length; i++) {
+			var obj = new Object();
+			for (var j=0; j < rcm['childs'][i]['childs'].length; j++) {
+				obj[rcm['childs'][i]['childs'][j]['name']] =
+					rcm['childs'][i]['childs'][j]['value'];
+			}
+			recommendations.push(obj);
+		}
+		if(recommendations.length < maximum) {
+			for(var i=0; i<recommendations.length; i++) {
+				clip['itm'+i]._x += 35*(maximum-recommendations.length);
+			}
+		}
+		showRecommendations(0);
+	};
+
+
+	/** Show the recommendations on screen **/
+	private function showRecommendations(off:Number) {
+		arguments.length == 1 ? offset = off: null;
+		offset == 0 ? clip.prv._visible = false: clip.prv._visible = true;
+		offset >= recommendations.length-maximum ? clip.nxt._visible = false: clip.nxt._visible = true;
+		for(var i=0; i<maximum; i++) {
+			clip['itm'+i].num = i+offset;
+			if(recommendations[i+offset] == undefined) {
+				clip['itm'+i]._visible = false;
+				clip['itm'+i]._alpha = 0;
+			} else {
+				clip['itm'+i].ldr.loadImage(recommendations[i+offset]['image']);
+			}
+		}
+		if(Stage['displayState'] == "fullScreen") {
+			clip._x = Math.round(Stage.width/2 - clip._width/2)-10;
+			clip._y = Stage.height-165;
+		} else {
+			clip._x = Math.round(config['displaywidth']/2 - clip._width/2)-10;
+			clip._y = config['displayheight']-85;
+		}
+	};
+
+
+	/** lower the list with related items **/
+	private function setState(stt:Number) {
+		if(stt == 3) {
+			if(recommendations == undefined) {
+				parser.parse(config['recommendations']);
+			} else {
+				showRecommendations();
+			}
+			clip._visible = true;
+			config['clip'].display.thumb._alpha = 33;
+		} else if (stt == 1 || stt == 2) {
+			clip._visible = false;
+			config['clip'].display.thumb._alpha = 100;
+		}
+	};
+
+
+	/** Set the title of the rolled over thumb. **/
+	private function setTitle(idx:Number) {
+		clip.txt.text = recommendations[idx]['title'];
+	};
+
+
+	/** Clear the title field again. **/
+	private function clearTitle() {
+		clip.txt.text = "";
+	};
+
+
+	/** Jump to the page with the requested file **/
+	private function getLink(idx:Number) {
+		getURL(recommendations[idx]['link'],config['linktarget']);
+	};
+
+
+	/** OnResize Handler: catches stage resizing. **/
+	public function onResize() {
+		if(config['displayheight'] >= config["height"]) {
+			config["height"] = config["displayheight"] = Stage.height;
+			config["width"] = config["displaywidth"] = Stage.width;
+		}
+		showRecommendations(); 
+	};
+
+
+	/** Catches fullscreen escape.  **/
+	public function onFullScreen(fs:Boolean) {
+		showRecommendations();
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/AbstractModel.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/AbstractModel.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/AbstractModel.as (revision 1)
@@ -0,0 +1,127 @@
+﻿/**
+* Abstract model class of the players MCV pattern, extended by all models.
+*
+* @author	Jeroen Wijering
+* @version	1.4
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.AbstractModel {
+
+
+	/** a list of all registered views **/
+	private var registeredViews:Array;
+	/** a reference to the controller **/
+	private var controller:AbstractController;
+	/** reference to the config array **/
+	private var config:Object;
+	/** reference to the feed array **/
+	private var feeder:Object;
+	/** item that's currently playing **/
+	private var currentItem:Number;
+	/** url of the item that's currently used by this model **/
+	private var currentURL:String;
+	/** array with extensions used by a model **/
+	private var mediatypes:Array;
+	/** boolean to check if a model is currently active **/
+	private var isActive:Boolean;
+	/** current playhead position **/
+	private var currentPosition:Number;
+
+
+	/** Constructor. **/
+	function AbstractModel(vws:Array,ctr:AbstractController,
+		cfg:Object,fed:Object) {
+		registeredViews = vws;
+		controller = ctr;
+		config = cfg;
+		feeder = fed;
+	};
+
+
+	/** Receive changes from the PlayerController. **/
+	public function getChange(typ:String,prm:Number):Void {
+		trace("model: "+typ+": "+prm);
+		switch(typ) {
+			case "item":
+				setItem(prm);
+				break;
+			case "start":
+				if(isActive == true) { setStart(prm); }
+				break;
+			case "pause":
+				if(isActive == true) { setPause(prm); }
+				break;
+			case "stop":
+				if(isActive == true) { setStop(); }
+				break;
+			case "volume":
+				setVolume(prm);
+				break;
+			default:
+				trace("Model: incompatible change received");
+				break;
+		}
+	};
+
+
+	/** Set new item and check if the model should be the active one. **/
+	private function setItem(idx:Number) {
+		currentItem = idx;
+		var fnd:Boolean = false;
+		for (var i=0; i<mediatypes.length; i++) {
+			if(feeder.feed[idx]["type"] == mediatypes[i]) {
+				fnd = true;
+			}
+		}
+		if(feeder.feed[idx]["start"] > 0) {
+			currentPosition = feeder.feed[idx]["start"];
+		}
+		if(fnd == true) {
+			isActive = true;
+			sendUpdate("item",idx);
+		} else {
+			isActive = false;
+		}
+	};
+
+
+	/** Start function. **/
+	private function setStart(prm:Number) {};
+
+
+	/** Pause function. **/
+	private function setPause(prm:Number) {};
+
+
+	/** Stop function. **/
+	private function setStop() {};
+
+
+	/** Set volume and pass through if active. **/
+	private function setVolume(vol:Number) { 
+		if(isActive == true) { sendUpdate("volume",vol); }
+	};
+
+
+	/** Send updates to the views. **/
+	private function sendUpdate(typ:String,prm:Number,pr2:Number) {
+		for(var i=0; i<registeredViews.length; i++) {
+			registeredViews[i].getUpdate(typ,prm,pr2);
+		}
+		if(typ == 'size') {
+			controller.getEvent(typ,prm,pr2);
+		}
+	};
+
+
+	/** Send a "complete" event directly to the controller. **/
+	private function sendCompleteEvent() {
+		controller.getEvent("complete"); 
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/RotatorView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/RotatorView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/RotatorView.as (revision 1)
@@ -0,0 +1,518 @@
+﻿/**
+* Rotator user interface View of the MCV cycle.
+*
+* @author	Jeroen Wijering
+* @version	1.5
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.ImageLoader;
+import com.jeroenwijering.utils.Animations;
+import flash.geom.Transform;
+import flash.geom.ColorTransform;
+
+class com.jeroenwijering.players.RotatorView extends AbstractView { 
+
+
+	/** full width of the scrubbars **/
+	private var currentItem:Number;
+	/** clip that's currently active **/
+	private var upClip:MovieClip;
+	/** clip that's currently inactive **/
+	private var downClip:MovieClip;
+	/** boolean for whether to use the title display **/ 
+	private var useTitle:Boolean;
+	/** boolean to see if the transition is done **/
+	private var transitionDone:Boolean = false;
+	/** boolean to detect first run **/ 
+	private var firstRun:Boolean = true;
+	/** interval for hiding the display **/
+	private var hideInt:Number;
+	/** array with all transitions **/ 
+	private var allTransitions:Array = new Array(
+		"bgfade",
+		"blocks",
+		"bubbles",
+		"circles",
+		"fade",
+		"flash",
+		"fluids",
+		"lines",
+		"slowfade"
+	);
+
+
+	/** Constructor **/
+	function RotatorView(ctr:AbstractController,cfg:Object,fed:Object) { 
+		super(ctr,cfg,fed);
+		setColorsClicks();
+		if(config["shownavigation"] == "true") {
+			Mouse.addListener(this);
+		}
+	};
+
+
+	/** Sets up visibility, sizes and colors of all display items **/
+	private function setColorsClicks() {
+		var ref = this;
+		var tgt:MovieClip = config["clip"];
+		tgt.button._width = config["width"];
+		tgt.button._height = config["height"];
+		if(config['overstretch']=='true' || config['overstretch']=='fit') {
+			tgt.img1.bg._visible = tgt.img2.bg._visible = false;
+		} else {
+			tgt.img1.bg._width = tgt.img2.bg._width = config["width"];
+			tgt.img1.bg._height = tgt.img2.bg._height = config["height"];
+			tgt.img1.col = new Color(tgt.img1.bg);
+			tgt.img1.col.setRGB(config["screencolor"]);
+			tgt.img2.col = new Color(tgt.img2.bg);
+			tgt.img2.col.setRGB(config["screencolor"]);
+		}
+		if(config["linkfromdisplay"] == "true") {
+			tgt.button.onRelease = function() { 
+				ref.sendEvent("getlink",ref.currentItem); 
+			};
+			tgt.playicon._visible = false;
+		} else {
+			tgt.button.onRelease = function() { 
+				ref.sendEvent("next"); 
+			};
+		}
+		tgt.img1.swapDepths(1);
+		tgt.img2.swapDepths(2);
+		tgt.playicon.swapDepths(4);
+		tgt.activity.swapDepths(5);
+		tgt.navigation.swapDepths(6);
+		tgt.logo.swapDepths(7);
+		tgt.playicon._x=tgt.activity._x = Math.round(config["width"]/2);
+		tgt.playicon._y=tgt.activity._y = Math.round(config["height"]/2);
+		if(config["logo"] != undefined) {
+			var lll = new ImageLoader(tgt.logo,"none");
+			lll.onLoadFinished = function() {
+				ref.config['clip'].logo._x = ref.config["displaywidth"] -
+					ref.config['clip'].logo._width -10;
+				ref.config['clip'].logo._y = 10;
+			};
+			lll.loadImage(config["logo"]);
+		}
+		tgt = config["clip"].navigation;
+		if (config["shownavigation"] == "true") {
+			tgt._y = config["height"] - 40;
+			tgt._x = config["width"]/2 - 50;
+			tgt.prevBtn.col1 = new Color(tgt.prevBtn.bck);
+			tgt.prevBtn.col1.setRGB(config["backcolor"]);
+			tgt.prevBtn.col2 = new Color(tgt.prevBtn.icn);
+			tgt.prevBtn.col2.setRGB(config["frontcolor"]);
+			tgt.itmBtn.col1 = new Color(tgt.itmBtn.bck);
+			tgt.itmBtn.col1.setRGB(config["backcolor"]);
+			tgt.itmBtn.txt.textColor = config["frontcolor"];
+			tgt.nextBtn.col1 = new Color(tgt.nextBtn.bck);
+			tgt.nextBtn.col1.setRGB(config["backcolor"]);
+			tgt.nextBtn.col2 = new Color(tgt.nextBtn.icn);
+			tgt.nextBtn.col2.setRGB(config["frontcolor"]);
+			tgt.prevBtn.onRollOver = tgt.nextBtn.onRollOver = function() { 
+				this.col2.setRGB(ref.config["lightcolor"]);
+			};
+			tgt.prevBtn.onRollOut = tgt.nextBtn.onRollOut = function() { 
+				this.col2.setRGB(ref.config["frontcolor"]);
+			};
+			tgt.itmBtn.onRollOver = function() {
+				this.txt.textColor = ref.config["lightcolor"];
+			};
+			tgt.itmBtn.onRollOut = function() {
+				this.txt.textColor = ref.config["frontcolor"];
+			};
+			tgt.prevBtn.onRelease = function() { 
+				ref.sendEvent("prev");
+				this.col2.setRGB(ref.config["frontcolor"]);
+			};
+			tgt.itmBtn.onRelease = function() { ref.sendEvent("playpause"); };
+			tgt.nextBtn.onRelease = function() { 
+				ref.sendEvent("next");
+				this.col2.setRGB(ref.config["frontcolor"]);
+			};
+			// set sizes, colors and buttons for image title
+			var len = 0;
+			for(var i=0; i<feeder.feed.length; i++) {
+				if(feeder.feed[i]['title'] != undefined && 
+					feeder.feed[i]['title'].length > len) {
+					len = feeder.feed[i]['title'].length;
+				} 
+			}
+			if(len == 0) {
+				useTitle = false; 
+				tgt.titleBtn._visible = false;
+			} else {
+				useTitle = true;
+				tgt.titleBtn._x = 74;
+				tgt.titleBtn.col1 = new Color(tgt.titleBtn.left);
+				tgt.titleBtn.col1.setRGB(config["backcolor"]);
+				tgt.titleBtn.col2 = new Color(tgt.titleBtn.mid);
+				tgt.titleBtn.col2.setRGB(config["backcolor"]);
+				tgt.titleBtn.col3 = new Color(tgt.titleBtn.right);
+				tgt.titleBtn.col3.setRGB(config["backcolor"]);
+				tgt.titleBtn.tf._width = len*6;
+				tgt.titleBtn.tf.textColor = config["frontcolor"];
+				if(feeder.feed[0]["link"] != undefined) {
+					tgt.titleBtn.onRollOver = function() {
+						this.tf.textColor = ref.config["lightcolor"];
+					};
+					tgt.titleBtn.onRollOut = function() {
+						this.tf.textColor = ref.config["frontcolor"];
+					};
+					tgt.titleBtn.onRelease = function() {
+						ref.sendEvent("getlink",ref.currentItem);
+					};
+				};
+				tgt.titleBtn.mid._width = len*6;
+				tgt.titleBtn.right._x = len*6+4;
+				tgt.nextBtn._x = len*6 + 79;
+			}
+			if(feeder.audio == true) {
+				tgt.audioBtn.col1 = new Color(tgt.audioBtn.bck);
+				tgt.audioBtn.col2 = new Color(tgt.audioBtn.icnOn);
+				tgt.audioBtn.col3 = new Color(tgt.audioBtn.icnOff);
+				tgt.audioBtn.col1.setRGB(config["backcolor"]);
+				tgt.audioBtn.col2.setRGB(config["frontcolor"]);
+				tgt.audioBtn.col3.setRGB(config["frontcolor"]);
+				tgt.audioBtn.onRollOver = function() {
+					this.col2.setRGB(ref.config["lightcolor"]);
+					this.col3.setRGB(ref.config["lightcolor"]);
+				};
+				tgt.audioBtn.onRollOut = function() {
+					this.col2.setRGB(ref.config["frontcolor"]);
+					this.col3.setRGB(ref.config["frontcolor"]);
+				};
+				tgt.audioBtn.onRelease = function() {
+					ref.sendEvent("audio");
+					this.col2.setRGB(ref.config["frontcolor"]);
+					this.col3.setRGB(ref.config["frontcolor"]);
+				};
+				if(config['useaudio'] == "true") {
+					tgt.audioBtn.icnOff._visible = false;
+				} else {
+					tgt.audioBtn.icnOn._visible = false;
+				}
+				tgt.audioBtn._x = len*6 + 104;
+			} else {
+				tgt.audioBtn._x = 0;
+				tgt.audioBtn._visible = false;
+			}	
+			tgt._x = Math.round(config["width"]/2 - tgt._width/2);
+		} else {
+			tgt._visible = false;
+		}
+	};
+
+
+	/** New item: switch clips and ready transition **/
+	private function setItem(pr1) {
+		currentItem = pr1;
+		transitionDone = false;
+		var tgt = config["clip"];
+		tgt.navigation.itmBtn.txt.text = (currentItem+1) + " / " + 
+			feeder.feed.length;
+		if (useTitle == true) {
+			tgt.navigation.titleBtn.tf.text=feeder.feed[currentItem]["title"];
+		}
+		tgt.img1.swapDepths(tgt.img2);
+		downClip = upClip;
+		if (upClip == tgt.img1) {
+			upClip = tgt.img2;
+		} else {
+			upClip = tgt.img1;
+		}
+	};
+
+
+	/** State switch; start the transition **/
+	private function setState(stt:Number) {
+		switch(stt) {
+			case 0:
+				if(config["showicons"] == "true") {
+					config["clip"].playicon._visible = true;
+				}
+				config["clip"].activity._visible = false;
+				break;
+			case 1:
+				config["clip"].playicon._visible = false;
+				if(config["showicons"] == "true") {
+					config["clip"].activity._visible = true;
+				}
+				break;
+			case 2:
+				config["clip"].playicon._visible = false;
+				config["clip"].activity._visible = false;
+				if(transitionDone == false) {
+					doTransition();
+					if(config["kenburns"] == "true") {
+						moveClip();
+					}
+				}
+				break;
+		}
+	};
+
+
+	/** (Re)set the ken burns fade **/
+	private function moveClip() {
+		var dir = random(4);
+		var clp = upClip.smc;
+		if(upClip.smc == undefined) { clp = upClip.mc; }
+		clp._xscale *= config['rotatetime']/20 + 1;
+		clp._yscale *= config['rotatetime']/20 + 1;
+		if(dir == 0) { 
+			clp._x = 0;
+		} else if (dir == 1) {
+			clp._y = 0;
+		} else if (dir == 2) {
+			clp._x = config['width'] - upClip._width;
+		} else {
+			clp._y = config['height'] - upClip._height;
+		}
+		clp.onEnterFrame = function() {
+			if(dir == 0) {
+				this._x -= 0.3;
+			} else if (dir == 1) {
+				this._y -= 0.3;
+			} else if (dir == 2) {
+				this._x += 0.3;
+			} else {
+				this._y += 0.3;
+			}
+		};
+	};
+
+
+	/** Start a transition **/
+	private function doTransition() {
+		transitionDone = true;
+		if(firstRun == true) {
+			config["clip"].img1._alpha = 100;
+			config["clip"].img2._alpha = 0;
+			firstRun = false;
+		} else {
+			var trs = config["transition"];
+			if(trs == "random") {
+				trs = allTransitions[random(allTransitions.length)];
+			}
+			switch (trs) {
+				case "bgfade":
+					doBGFade();
+					break;
+				case "blocks":
+					doBlocks();
+					break;
+				case "bubbles":
+					doBubbles();
+					break;
+				case "circles":
+					doCircles();
+					break;
+				case "fade":
+					doFade();
+					break;
+				case "flash":
+					doFlash();
+					break;
+				case "fluids":
+					doFluids();
+					break;
+				case "lines":
+					doLines();
+					break;
+				case "slowfade":
+					doSlowfade();
+					break;
+				default:
+					doFade();
+					break;
+			}
+		}
+	};
+
+
+	/** Function for the fade transition **/
+	private function doFade() {
+		upClip.ref = this;
+		upClip._alpha = 0;
+		upClip.onEnterFrame = function() {
+			this._alpha +=5;
+			if(this._alpha >= 100) {
+				delete this.onEnterFrame;
+				this.ref.downClip._alpha = 0;
+			}
+		};
+	};
+
+
+	/** Function for the bgfade transition **/
+	private function doBGFade() {
+		downClip.ref = upClip.ref = this;
+		downClip.onEnterFrame = function() {
+			this._alpha -=5;
+			if(this._alpha <= 0) {
+				delete this.onEnterFrame;
+				this.ref.upClip.onEnterFrame = function() {
+					if(this._alpha >= 100) {
+						delete this.onEnterFrame;
+					} else {
+						this._alpha +=5;
+					}
+				};
+			}
+		};
+	};
+
+
+	/** Function for the blocks transition **/
+	private function doBlocks() {
+		upClip._alpha = 100;
+		config["clip"].attachMovie("blocksMask","mask",3);
+		var msk:MovieClip = config["clip"].mask;
+		if (config["width"] > config["height"]) {
+			msk._width = msk._height = config["width"];
+		} else {
+			msk._width = msk._height = config["height"];
+		}
+		msk._rotation = random(4)*90;
+		msk._rotation == 90 ? msk._x = config["width"]: null;
+		msk._rotation == 180 ? msk._x = config["width"]: null;
+		msk._rotation == 180 ? msk._y = config["height"]: null;
+		msk._rotation == -90 ? msk._y = config["height"]: null;
+		upClip.setMask(msk);
+		playClip(msk);
+	}; 
+
+
+	/** Function for the bubbles transition **/
+	private function doBubbles() {
+		upClip._alpha = 100;
+		config["clip"].attachMovie("bubblesMask","mask",3);
+		var msk:MovieClip = config["clip"].mask;
+		upClip.setMask(msk);
+		if (config["width"] > config["height"]) {
+			msk._width = msk._height = config["width"];
+			msk._y = config["height"]/2 - msk._height/2;
+		} else {
+			msk._width = msk._height = config["height"];
+			msk._x = config["width"]/2- msk._width/2;
+		}
+		if(random(2) == 1) { 
+			msk._xscale = -msk._xscale; 
+			msk._x += config['width']; 
+		}
+		playClip(msk);
+	};
+
+
+	/** Function for the circles transition **/
+	private function doCircles() {
+		upClip._alpha = 100;
+		config["clip"].attachMovie("circlesMask","mask",3);
+		var msk:MovieClip = config["clip"].mask;
+		upClip.setMask(msk);
+		if (config["width"] > config["height"]) {
+			msk._width = msk._height = config["width"];
+		} else {
+			msk._width = msk._height = config["height"];
+		}
+		msk._x = config["width"]/2;
+		msk._y = config["height"]/2;
+		playClip(msk,10);
+	};
+
+
+	/** Function for the flash transition **/
+	private function doFlash() {
+		upClip._alpha = 100;
+		upClip.col = new Color(upClip);
+		upClip.ctf = new Object({rb:255,gb:255,bb:255});
+		upClip.col.setTransform(upClip.ctf);
+		upClip.onEnterFrame = function() {
+			if(this.ctf.rb < 1) {
+				this.ctf =  new Object({rb:0,gb:0,bb:0});
+				this.col.setTransform(this.ctf);
+				delete this.onEnterFrame;
+			} else {
+				this.ctf.rb /= 1.05;
+				this.ctf.gb /= 1.05;
+				this.ctf.bb /= 1.05;
+				this.col.setTransform(this.ctf);
+			}
+		};
+	};
+
+	/** Function for the fluids transition **/
+	private function doFluids() {
+		upClip._alpha = 100;
+		config["clip"].attachMovie("fluidsMask","mask",3);
+		var msk:MovieClip = config["clip"].mask;
+		upClip.setMask(msk);
+		msk._width = config["width"];
+		msk._height = config["height"];
+		playClip(msk);
+	};
+
+
+	/** Function for the lines transition **/
+	private function doLines() {
+		upClip._alpha = 100;
+		config["clip"].attachMovie("linesMask","mask",3);
+		var msk:MovieClip = config["clip"].mask;
+		upClip.setMask(msk);
+		msk._width = config["width"];
+		msk._height = config["height"];
+		playClip(msk);
+	};
+
+
+	/** Function for the fade transition **/
+	private function doSlowfade() {
+		upClip.ref = this;
+		upClip._alpha = 0;
+		upClip.onEnterFrame = function() {
+			this._alpha+=2;
+			if(this._alpha >= 100) {
+				delete this.onEnterFrame;
+				this.ref.downClip._alpha = 0;
+			}
+		};
+	};
+
+
+	/** Play a specific Movieclip and remove it once it's finished **/
+	private function playClip(tgt:MovieClip,rot:Number) {
+		tgt.ref = this;
+		tgt.onEnterFrame = function() {
+			this.nextFrame();
+			rot == undefined ? null: this._rotation +=rot;
+			if(this._currentframe  == this._totalframes) {
+				this.ref.downClip._alpha = 0;
+				this.clear();
+				this.unloadMovie();
+				this.removeMovieClip();
+			}
+		};
+	};
+
+
+	/** after a delay, the controlbar is hidden **/
+	private function hideBar() {
+		Animations.fadeOut(config['clip'].navigation);
+		clearInterval(hideInt);
+	}
+
+
+	/** Mouse move shows controlbar **/
+	public function onMouseMove() {
+		Animations.fadeIn(config['clip'].navigation);
+		clearInterval(hideInt);
+		if(!config["clip"].navigation.hitTest(_root._xmouse,_root._ymouse)) {
+			hideInt = setInterval(this,"hideBar",500);
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/CallbackView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/CallbackView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/CallbackView.as (revision 1)
@@ -0,0 +1,93 @@
+﻿/**
+* Callback to serverside script for statistics handling.
+* It sends the current file,title,id and state on start and complete.
+*
+* @author	Jeroen Wijering
+* @author	Nate Hanna
+* @version	1.7
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.CallbackView extends AbstractView { 
+
+
+	/** Currently playing item **/
+	private var currentItem:Number;
+	/** Currently playing item **/
+	private var varsObject:LoadVars;
+	/** Boolean for if a start call has already been sent for an item. **/
+	private var playSent:Boolean = false;
+	/** Small interval so both complete and play events won't be issued **/
+	private var playSentInt:Number;
+	/** Timestamp of the start of the movie **/
+	private var startStamp:Number;
+
+
+	/** Constructor **/
+	function CallbackView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		if(config['callback'] != "analytics") {
+			varsObject = new LoadVars();
+		}
+	};
+
+
+	/** Send a callback on state change **/
+	private function setState(pr1:Number) {
+		var dat = new Date();
+		if(pr1 == 3) {
+			var dur = Math.round(dat.valueOf()/1000 - startStamp);
+			sendVars("stop",dur,true);
+			playSent = false;
+		} else if (pr1 == 2 && playSent == false) {
+			playSentInt = setInterval(this,"sendVars",500,"start",0);
+			playSent = true;
+			startStamp = dat.valueOf()/1000;
+		}
+	};
+
+
+	/** save the currently playing item **/
+	private function setItem(pr1:Number) {
+		if(playSent == true && currentItem != undefined)  {
+			var dat = new Date();
+			var dur = Math.round(dat.valueOf()/1000 - startStamp);
+			sendVars("stop",dur,false);
+			playSent = false;
+		}
+		currentItem = pr1; 
+	};
+
+
+	/** sending the current file,title,id,state,timestamp to callback **/
+	private function sendVars(stt:String,dur:Number,cpl:Boolean) {
+		clearInterval(playSentInt);
+		if(config['callback'] == "urchin" || config['callback'] == "analytics") {
+			var fil = feeder.feed[currentItem]["file"];
+			var fcn = "javascript:pageTracker._trackPageview";
+			if(config['callback'] == "urchin") {
+				fcn = "javascript:urchinTracker";
+			}
+			if(fil.indexOf('http') != undefined) {
+				fil = fil.substring(fil.indexOf('/',7)+1);
+			}
+			if(stt == "start") {
+				getURL(fcn+"('/start_stream/"+fil+"');");
+			} else if (stt == "stop" && cpl == true) {
+				getURL(fcn+"('/end_stream/"+fil+"');");
+			}
+		} else {
+			varsObject.file = feeder.feed[currentItem]["file"];
+			varsObject.title = feeder.feed[currentItem]["title"];
+			varsObject.id = feeder.feed[currentItem]["id"];
+			varsObject.state = stt;
+			varsObject.duration = dur;
+			varsObject.sendAndLoad(config["callback"],varsObject,"POST");
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/InputView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/InputView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/InputView.as (revision 1)
@@ -0,0 +1,63 @@
+﻿/**
+* Keyboard input management of the players MCV pattern.
+* SPACE: playpause,UP:prev,DOWN:next,LEFT:volume-10,RIGHT:volume+10
+*
+* @author	Jeroen Wijering
+* @version	1.3
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.InputView extends AbstractView { 
+
+
+	/** The current volume **/
+	private var currentVolume:Number;
+	/** The current elapsed time **/
+	private var currentTime:Number;
+
+
+	/** Constructor **/
+	function InputView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		Key.addListener(this);
+	};
+
+
+	/** Save current elapsed time **/
+	private function setTime(elp:Number,rem:Number) { currentTime = elp; };
+
+
+	/** Save current volume **/
+	private function setVolume(vol:Number) { currentVolume = vol; };
+
+
+	/** KeyDown handler, forwarded by Key object **/
+	public function onKeyDown() {
+		if (Key.getCode() == 32 && SearchView.focussed != true) {
+			sendEvent("playpause"); 
+		} else if (Key.getCode() == 37) {
+			if(feeder.feed.length == 1) {
+				sendEvent("scrub",currentTime-15);
+			} else {
+				 sendEvent("prev");
+			}
+		} else if (Key.getCode() == 39) {
+			if(feeder.feed.length == 1) {
+				sendEvent("scrub",currentTime+15);
+			} else {
+				sendEvent("next");
+			}
+		} else if (Key.getCode() == 38) {
+			sendEvent("volume",currentVolume+10);
+		} else if (Key.getCode() == 40) {
+			sendEvent("volume",currentVolume-10);
+		} else if (Key.getCode() == 77) {
+			sendEvent("volume",0);
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/EqualizerView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/EqualizerView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/EqualizerView.as (revision 1)
@@ -0,0 +1,98 @@
+﻿/**
+* View for an actionscript-drawn equalizer (thanks to Brewer).
+* The eq. is fake, but it considers playstate and volume.
+*
+* @author	Jeroen Wijering
+* @version	1.1
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.EqualizerView extends AbstractView {
+
+
+	/** EQ movieclip reference **/
+	private var eqClip:MovieClip;
+	/** current volume **/
+	private var currentVolume:Number;
+	/** number of stripes to display in the EQ **/
+	private var eqStripes:Number;
+
+
+	/** Constructor; just inheriting. **/
+	function EqualizerView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		setupEQ();
+		Stage.addListener(this);
+	};
+
+
+	/** setup EQ **/
+	private function setupEQ() {
+		eqClip = config["clip"].equalizer;
+		eqClip._y = config["displayheight"] - 50;
+		eqStripes = Math.floor((config['displaywidth'] - 20)/6);
+		eqClip.stripes.duplicateMovieClip("stripes2",1);
+		eqClip.mask.duplicateMovieClip("mask2",3);
+		eqClip.stripes._width = eqClip.stripes2._width = 
+			config['displaywidth']-20;
+		eqClip.stripes.top.col = new Color(eqClip.stripes.top);
+		eqClip.stripes.top.col.setRGB(config['lightcolor']);
+		eqClip.stripes.bottom.col = new Color(eqClip.stripes.bottom);
+		eqClip.stripes.bottom.col.setRGB(0xFFFFFF);
+		eqClip.stripes2.top.col = new Color(eqClip.stripes2.top);
+		eqClip.stripes2.top.col.setRGB(config['lightcolor']);
+		eqClip.stripes2.bottom.col = new Color(eqClip.stripes2.bottom);
+		eqClip.stripes2.bottom.col.setRGB(0xFFFFFF);
+		eqClip.stripes.setMask(eqClip.mask);
+		eqClip.stripes2.setMask(eqClip.mask2);
+		eqClip.stripes._alpha = eqClip.stripes2._alpha = 50;
+		setInterval(this,"drawEqualizer",100,eqClip.mask);
+		setInterval(this,"drawEqualizer",100,eqClip.mask2);
+	};
+
+
+	/** Draw a random frame for the equalizer **/
+	private function drawEqualizer(tgt:MovieClip) {
+		tgt.clear();
+	    tgt.beginFill(0x000000, 100);
+		tgt.moveTo(0,0);
+		var h = Math.round(currentVolume/4);
+		for (var j=0; j< eqStripes; j++) {
+			var z = random(h)+h/2 + 2;
+			if(j == Math.floor(eqStripes/2)) { z = 0; }
+			tgt.lineTo(j*6,-1);
+			tgt.lineTo(j*6,-z);
+			tgt.lineTo(j*6+4,-z);
+			tgt.lineTo(j*6+4,-1);
+			tgt.lineTo(j*6,-1); 
+		}
+		tgt.lineTo(eqStripes*6,0);
+		tgt.lineTo(0,0);
+		tgt.endFill();
+	};
+
+
+	/** Change the height to reflect the volume **/
+	private function setVolume(vol:Number) { currentVolume = vol; };
+
+
+	/** Only display the eq if a song is playing **/
+	private function setState(stt:Number) { 
+		stt == 2 ? eqClip._visible = true: eqClip._visible = false;
+	};
+
+
+	/** Hide the EQ on fullscreen view  **/
+	public function onFullScreen(fs:Boolean) { 
+		if(fs == true) {
+			eqClip._visible = false;
+		} else {
+			eqClip._visible = true;
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/AudioView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/AudioView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/AudioView.as (revision 1)
@@ -0,0 +1,103 @@
+﻿/**
+* Extra audiotrack management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.1
+**/
+
+
+import com.jeroenwijering.players.*;
+
+class com.jeroenwijering.players.AudioView extends AbstractView { 
+
+
+	/** The MovieClip to which the sounds will be attached **/
+	private var audioClip:MovieClip;
+	/** The Sound object we'll use**/
+	private var audioObject:Sound;
+	/** Currently active feeditem **/
+	private var currentItem:Number;
+	/** The current elapsed time **/
+	private var currentTime:Number = 0;
+	/** The last stop position **/
+	private var stopTime:Number;
+	/** The current audio time **/
+	private var audioTime:Number;
+	/** Save the current state **/
+	private var currentState:Number;
+	/** Check whether an MP3 file is loaded **/
+	private var isLoaded:String;
+	/** Sync the audio with emtry or not **/
+	private var sync:Boolean;
+
+
+	/** Constructor, loads caption file. **/
+	function AudioView(ctr:AbstractController,cfg:Object,fed:Object,
+		snc:Boolean) {
+		super(ctr,cfg,fed);
+		sync = snc;
+		var ref = this;
+		audioClip = config['clip'].createEmptyMovieClip('audio',
+			config['clip'].getNextHighestDepth());
+		audioClip.setStart = function() {
+			if(ref.stopTime == undefined && ref.sync == false) {
+				ref.audioObject.loadSound(ref.feeder.feed[0]['audio'],true);
+				ref.audioObject.setVolume(Number(ref.config['volume']));
+				ref.audioObject.start(0);
+			} else if (ref.sync == false) {
+				ref.audioObject.start(ref.stopTime);
+			} else if(ref.currentState == 2) {
+				ref.audioObject.start(ref.currentTime);
+			}
+		};
+		audioClip.setStop = function() { 
+			ref.audioObject.stop();
+			ref.stopTime = ref.audioObject.position/1000;
+		};
+		audioObject = new Sound (audioClip);
+		if(config['useaudio'] == "true" && sync == false) { 
+			audioClip.setStart();
+		}
+		if(sync == false) {
+			audioObject.onSoundComplete = function() {
+				this.start();
+			};
+		}
+	};
+
+
+	private function setItem(idx:Number) { 
+		currentItem = idx;
+	};
+
+
+	private function setState(stt:Number) {
+		currentState = stt;
+		if(sync == false) { return; }
+		if(stt == 2 && config['useaudio'] == "true") {
+			audioObject.start(currentTime);
+		} else {
+			audioObject.stop();
+		}
+	};
+
+
+	private function setTime(elp:Number,rem:Number) {
+		if(sync == false) { return; }
+		if(Math.abs(elp-currentTime) > 1) {
+			currentTime = elp;
+			audioTime = audioObject.position/1000;
+			if(Math.abs(currentTime - audioTime) > 1 &&
+				config['useaudio'] == "true") {
+				audioObject.start(currentTime);
+			}
+		}
+		if (isLoaded != feeder.feed[currentItem]['audio']) {
+			isLoaded = feeder.feed[currentItem]['audio'];
+			audioObject.loadSound(isLoaded,true);
+			audioObject.setVolume(Number(config['volume']));
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/MediaPlayer.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/MediaPlayer.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/MediaPlayer.as (revision 1)
@@ -0,0 +1,198 @@
+﻿/**
+* Player that reads all media formats Flash can read.
+*
+* @author	Jeroen Wijering
+* @version	1.10
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.BandwidthCheck;
+
+
+class com.jeroenwijering.players.MediaPlayer extends AbstractPlayer {
+
+
+	/** Array with all config values **/
+	private var config:Object = {
+		clip:undefined,
+		height:260,
+		width:320,
+		controlbar:20,
+		displayheight:undefined,
+		displaywidth:undefined,
+		searchbar:'true',
+
+		file:undefined,
+		fallback:undefined,
+		image:undefined,
+		link:undefined,
+		id:undefined,
+		type:undefined,
+		captions:undefined,
+		audio:undefined,
+		category:undefined,
+
+		frontcolor:0x000000,
+		backcolor:0xffffff,
+		lightcolor:0x000000,
+		screencolor:0x000000,
+
+		autoscroll:"false",
+		largecontrols:"false",
+		logo:undefined,
+		showdigits:'true',
+		showdownload:'false',
+		showeq:'false',
+		showicons:'true',
+		shownavigation:'true',
+		showstop:'false',
+		thumbsinplaylist:'true',
+		usefullscreen:'true',
+		fsbuttonlink:undefined,
+
+		autostart:'false',
+		bufferlength:3,
+		overstretch:'false',
+		repeat:'list',
+		rotatetime:5,
+		shuffle:'false',
+		smoothing:'true',
+		volume:80,
+
+		bwfile:"100k.jpg",
+		bwstreams:undefined,
+		callback:undefined,
+		enablejs:'false',
+		javascriptid:'',
+		linkfromdisplay:'false',
+		linktarget:'_blank',
+		midroll:undefined,
+		prefix:'',
+		recommendations:undefined,
+		searchlink:'http://search.longtail.tv/?q=',
+		streamscript:undefined,
+		useaudio:'true',
+		usecaptions:'true',
+		usemute:'false',
+		usekeys:'true',
+
+		abouttxt:'JW Player 3.16',
+		aboutlnk:'http://www.jeroenwijering.com/?about=JW_FLV_Media_Player'
+	};
+
+
+	/** Constructor **/
+	public function MediaPlayer(tgt:MovieClip) {
+		super(tgt);
+	};
+
+
+	/** check bandwidth for streaming **/
+	private function checkStream() {
+		var ref = this;
+		var str = config["bwstreams"].split(",");
+		var bwc = new BandwidthCheck(config["bwfile"]);
+		bwc.onComplete = function(kbps) {
+			trace("bandwidth: "+kbps);
+			var bwc = new ContextMenuItem("Detected bandwidth: "+kbps+" kbps");
+			bwc.separatorBefore = true;
+			ref.manager.context.customItems.push(bwc);
+			if(ref.config['enablejs'] == "true" && 
+				flash.external.ExternalInterface.available) {
+				flash.external.ExternalInterface.call("getBandwidth",kbps);
+			}
+			for (var i=1; i<str.length; i++) {
+				if (kbps < Number(str[i])) {
+					ref.loadFile(str[i-1]);
+					return;
+				}
+			}
+			ref.loadFile(str[str.length-1]);
+		};
+	};
+
+
+	/** Setup all necessary MCV blocks. **/
+	private function setupMCV() {
+		// set controller
+		controller = new PlayerController(config,feeder);
+		// set default views
+		var dpv = new DisplayView(controller,config,feeder);
+		var vws = new Array(dpv);
+		if(config['shownavigation'] == "true") {
+			var cbv = new ControlbarView(controller,config,feeder);
+			vws.push(cbv);
+		} else {
+			config['clip'].controlbar._visible = false;
+		}
+		// set optional views
+		if(config["displayheight"] < config["height"]-config['controlbar']-config['searchbar'] ||
+			config["displaywidth"] < config["width"]) {
+			var plv = new PlaylistView(controller,config,feeder);
+			vws.push(plv);
+		} else {
+			config["clip"].playlist._visible = 
+				config["clip"].playlistmask._visible  = false;
+		}
+		if(config["usekeys"] == "true") {
+			var ipv = new InputView(controller,config,feeder);
+			vws.push(ipv);
+		}
+		if(config["showeq"] == "true") {
+			var eqv = new EqualizerView(controller,config,feeder);
+			vws.push(eqv);
+		} else {
+			config["clip"].equalizer._visible = false;
+		}
+		var cpv = new CaptionsView(controller,config,feeder);
+		vws.push(cpv);
+		if(config['recommendations'] != undefined) {
+			var rlv = new RecommendationsView(controller,config,feeder);
+			vws.push(rlv);
+		} else {
+			config["clip"].recommendations._visible = false;
+		}
+		if(config['searchbar'] > 0) {
+			var sev = new SearchView(controller,config,feeder);
+			vws.push(sev);
+		} else {
+			config["clip"].search._visible = false;
+		}
+		if(config['midroll'] != undefined) {
+			var mrv = new MidrollView(controller,config,feeder);
+			vws.push(mrv);
+		} else {
+			config["clip"].midroll._visible = false;
+		}
+		if(feeder.audio == true) {
+			var adv = new AudioView(controller,config,feeder,true);
+			vws.push(adv);
+		}
+		if(config["enablejs"] == "true") {
+			var jsv = new JavascriptView(controller,config,feeder);
+			vws.push(jsv);
+		}
+		if(config["callback"] != undefined) {
+			var cav = new CallbackView(controller,config,feeder);
+			vws.push(cav);
+		}
+		// set models
+		var mp3 = new MP3Model(vws,controller,config,feeder,config["clip"]);
+		var flv = new FLVModel(vws,controller,config,feeder,config["clip"].display.video);
+		var img = new ImageModel(vws,controller,config,feeder,config["clip"].display.image);
+		var ytm = new YoutubeModel(vws,controller,config,feeder,config["clip"].display.youtube);
+		var mds:Array = new Array(mp3,flv,img,ytm);
+		if(feeder.captions == true) { flv.capView = cpv; }
+		// start mcv cycle
+		controller.startMCV(mds);
+	};
+
+
+	/** Application startup, used for MTASC compilation **/
+	public static function main() {
+		var mpl = new MediaPlayer(_root.player);
+	}
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/PlaylistView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/PlaylistView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/PlaylistView.as (revision 1)
@@ -0,0 +1,238 @@
+﻿/**
+* Playlist view management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.9
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.*;
+import com.jeroenwijering.feeds.FeedListener;
+
+
+class com.jeroenwijering.players.PlaylistView extends AbstractView 
+	implements FeedListener { 
+
+
+	/** ImageLoader **/
+	private var thumbLoader:ImageLoader;
+	/** Scroller instance **/
+	private var listScroller:Scroller;
+	/** Position of the playlist **/
+	private var listRight:Boolean;
+	/** Position of the playlist **/
+	private var listWidth:Number;
+	/** number of items in the playlist **/
+	private var listLength:Number;
+	/** Currently highlighted playlist item **/
+	private var currentItem:Number;
+	/** Save the current time **/
+	private var currentTime:Number = -10;
+	
+
+
+	/** Constructor **/
+	function PlaylistView(ctr:AbstractController,cfg:Object,fed:Object) { 
+		super(ctr,cfg,fed);
+		if(config["displaywidth"] < config["width"]) { 
+			listRight = true;
+			listWidth = config["width"]-config["displaywidth"]-1;
+		} else {
+			listRight = false;
+			listWidth = config["width"];
+		}
+		setButtons();
+		Stage.addListener(this);
+		feeder.addListener(this);
+	};
+
+
+	/** OnLoad event handler; sets up the playlist sizes and colors. **/
+	private function setButtons() {
+		var ref = this;
+		var tgt = config["clip"].playlist;
+		tgt.btn._visible = false;
+		// iterate playlist and setup each button
+		listLength = feeder.feed.length;
+		var num = 0;
+		for(var i=0; i<feeder.feed.length; i++) {
+			if(feeder.feed[i]['category'] != 'commercial' && 
+				feeder.feed[i]['category'] != 'preroll' && 
+				feeder.feed[i]['category'] != 'postroll') {
+			// set text and background
+			tgt.btn.duplicateMovieClip("btn"+i,i);
+			tgt["btn"+i].txt._width = listWidth - 20;
+			tgt["btn"+i].col = new Color(tgt["btn"+i].bck);
+			tgt["btn"+i].col.setRGB(config["frontcolor"]);
+			tgt["btn"+i].col2 = new Color(tgt["btn"+i].icn);
+			tgt["btn"+i].col2.setRGB(config["frontcolor"]);
+			tgt["btn"+i].bck._width = listWidth;
+			tgt["btn"+i].bck.onRollOver = function() { 
+				this._parent.txt.textColor = ref.config["backcolor"];
+				this._parent.col.setRGB(ref.config["lightcolor"]);
+				this._parent.col2.setRGB(ref.config["backcolor"]);
+				if(ref.currentItem != this._parent.getDepth()) {
+					this._alpha = 90;
+				}
+			};
+			tgt["btn"+i].bck.onRollOut = function() { 
+				this._parent.col.setRGB(ref.config["frontcolor"]);
+				if(ref.currentItem != this._parent.getDepth()) {
+					this._parent.txt.textColor=ref.config["frontcolor"];
+					this._parent.col2.setRGB(ref.config["frontcolor"]);
+					this._alpha = 10;
+				}
+			};
+			tgt["btn"+i].bck.onRelease = function() {
+				ref.sendEvent("playitem",this._parent.getDepth());
+			};
+			// set thumbnails
+			if(config["thumbsinplaylist"] == "true") {
+				tgt["btn"+i].bck._height = 40;
+				tgt["btn"+i].icn._y += 9;
+				tgt["btn"+i]._y = num*41;
+				tgt["btn"+i].txt._height += 20;
+				if(feeder.feed[i]["author"]  == undefined) {
+					tgt["btn"+i].txt.htmlText = "<b>"+(i+1)+"</b>:<br />"+
+						feeder.feed[i]["title"];
+				} else {
+					tgt["btn"+i].txt.htmlText = "<b>" + 
+						feeder.feed[i]["author"] + "</b>:<br />" + 
+						feeder.feed[i]["title"];
+				}
+				if(feeder.feed[i]["image"] != undefined) {
+					tgt["btn"+i].txt._x += 60;
+					tgt["btn"+i].txt._width -= 60;
+					thumbLoader = 
+						new ImageLoader(tgt["btn"+i].img,"true",60,40);
+					thumbLoader.loadImage(feeder.feed[i]["image"]);
+					tgt["btn"+i].img.setMask(tgt["btn"+i].msk);
+				} else {
+					tgt["btn"+i].msk._height = 10;
+					tgt["btn"+i].img._visible = false;
+					tgt["btn"+i].msk._visible = false;
+				}
+			} else {
+				tgt["btn"+i]._y = num*23;
+				if(feeder.feed[i]["author"]  == undefined) {
+					tgt["btn"+i].txt.htmlText = feeder.feed[i]["title"];
+				} else {
+					tgt["btn"+i].txt.htmlText = feeder.feed[i]["author"] +
+						" - " + feeder.feed[i]["title"];
+				}
+				tgt["btn"+i].msk._height = 10;
+				tgt["btn"+i].img._visible = 
+					tgt["btn"+i].msk._visible = false;
+			}
+			tgt["btn"+i].txt.textColor = config["frontcolor"];
+			// set link icon
+			if(feeder.feed[i]["link"] != undefined) {
+				tgt["btn"+i].txt._width -= 20;
+				tgt["btn"+i].icn._x = listWidth - 24;
+				tgt["btn"+i].icn.onRollOver = function() { 
+					this._parent.col2.setRGB(ref.config["lightcolor"]);
+				};
+				tgt["btn"+i].icn.onRollOut = function() { 
+					if(ref.currentItem == this._parent.getDepth()) {
+					this._parent.col2.setRGB(ref.config["backcolor"]);
+					} else {
+					this._parent.col2.setRGB(ref.config["frontcolor"]);
+					}
+				};
+				tgt["btn"+i].icn.onRelease = function() { 
+					ref.sendEvent("getlink",this._parent.getDepth());
+				};
+			} else { 
+				tgt["btn"+i].icn._visible = false;
+			}
+			num++;
+		} 
+		}
+		// setup mask and scrollbar if needed
+		var msk = config["clip"].playlistmask;
+		if(listRight == true) { 
+			msk._x = tgt._x = Number(config["displaywidth"]) + 1;
+			msk._y = tgt._y = 0;
+			msk._height =  config["displayheight"];
+		} else {
+			msk._y = tgt._y = config["displayheight"] + 
+				config["controlbar"] + config["searchbar"];
+			msk._height = config["height"] - msk._y;
+		}
+		msk._width = listWidth;
+		tgt.setMask(msk);
+		if(tgt._height > msk._height + 2 && feeder.feed.length > 1) {
+			if(config["autoscroll"] == "false") {
+				msk._width -= 10;
+				for(var i=0; i<feeder.feed.length; i++) {
+					tgt["btn"+i].bck._width -= 10;
+					tgt["btn"+i].icn._x -= 10;
+				}
+				listScroller = new Scroller(tgt,msk,false,
+					config["frontcolor"],config["lightcolor"]);
+			} else {	
+				listScroller = new Scroller(tgt,msk,true,
+					config["frontcolor"],config["lightcolor"]);
+			}
+		}
+	};
+
+
+	/** Set a new item as the current playing one **/
+	private function setItem(itm:Number):Void {
+		var tgt = config["clip"].playlist;
+		tgt["btn"+currentItem].col.setRGB(config["frontcolor"]);
+		tgt["btn"+currentItem].bck._alpha = 10;
+		tgt["btn"+currentItem].col2.setRGB(config["frontcolor"]);
+		tgt["btn"+currentItem].txt.textColor = config["frontcolor"];
+		currentItem = itm;
+		tgt["btn"+currentItem].txt.textColor = config["backcolor"];
+		tgt["btn"+currentItem].col2.setRGB(config["backcolor"]);
+		tgt["btn"+currentItem].bck._alpha = 90;
+		if(config["autoscroll"] == "false") {
+			listScroller.scrollTo(tgt["btn"+currentItem]._y);
+		}
+	};
+
+
+	/** Set a different chapter if the feed is a chapterindex **/
+	private function setTime(elp:Number,rem:Number) {
+		if(feeder.ischapters == true && Math.abs(elp-currentTime) > 5) {
+			currentTime = elp;
+			for (var i=0; i<feeder.feed.length; i++) {
+				if(feeder.feed[i]["start"] > currentTime) {
+					if(i != currentItem+1) { setItem(i-1); }
+					break;
+				}
+			}
+		}
+	};
+
+
+	/** Hide the scrollbar on fullscreen **/
+	public function onFullScreen(fs:Boolean) {
+		if(listScroller == undefined) {
+			break;
+		} else if(fs == true) {
+			config["clip"].scrollbar._visible = false;
+		} else {
+			config["clip"].scrollbar._visible = true; 
+		}
+	};
+
+
+	/** Render a new playlist when the feed updates **/
+	public function onFeedUpdate(typ:String) {
+		listScroller.purgeScrollbar();
+		delete listScroller;
+		var tgt = config["clip"].playlist;
+		for(var i=0; i<999; i++) {
+			tgt["btn"+i].removeMovieClip();
+		}
+		setButtons();
+		setItem(currentItem);
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/ImageRotator.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/ImageRotator.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/ImageRotator.as (revision 1)
@@ -0,0 +1,94 @@
+﻿/**
+* Manages startup and overall control of the Flash Image Rotator
+*
+* @author	Jeroen Wijering
+* @version	1.7
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.ImageRotator extends AbstractPlayer { 
+
+
+	/** Array with all config values **/
+	public var config:Object = {
+		clip:undefined,
+		height:200,
+		width:400,
+		
+		file:undefined,
+		image:undefined,
+		link:undefined,
+		id:undefined,
+		type:undefined,
+		captions:undefined,
+		audio:undefined,
+
+		backcolor:0x000000,
+		frontcolor:0xffffff,
+		lightcolor:0xffffff,
+		screencolor:0x000000,
+
+		kenburns:"false",
+		logo:undefined,
+		overstretch:"false",
+		showicons:"true",
+		shownavigation:"true",
+		transition:"random",
+
+		autostart:"true",
+		repeat:"true",
+		rotatetime:5,
+		shuffle:"true",
+		volume:80,
+
+		enablejs:"false",
+		javascriptid:undefined,
+		linkfromdisplay:"false",
+		linktarget:"_self",
+		useaudio:"true",
+
+		abouttxt:"JW Image Rotator 3.16",
+		aboutlnk:"http://www.jeroenwijering.com/?about=JW_Image_Rotator"
+	};
+
+
+	/** Constructor **/
+	function ImageRotator(tgt:MovieClip) { 
+		super(tgt);
+	};
+
+
+	/** Setup all necessary MCV blocks. **/
+	private function setupMCV():Void {
+		controller = new RotatorController(config,feeder);
+		var rov = new RotatorView(controller,config,feeder);
+		var ipv = new InputView(controller,config,feeder);
+		var vws:Array = new Array(rov,ipv);
+		if(config["enablejs"] == "true") {
+			var jsv = new JavascriptView(controller,config,feeder);
+			vws.push(jsv);
+		}
+		if(feeder.audio == true) {
+			var bav = new AudioView(controller,config,feeder,false);
+			vws.push(bav);
+		}
+		config["displayheight"] = config["height"];
+		var im1=new ImageModel(vws,controller,config,feeder,
+			config["clip"].img1,true);
+		var im2=new ImageModel(vws,controller,config,feeder,
+			config["clip"].img2,true);
+		var mds:Array = new Array(im1,im2);
+		controller.startMCV(mds);
+	};
+
+
+	/** Application startup, used for MTASC compilation **/
+	public static function main() {
+		var irt = new ImageRotator(_root.rotator);
+	}
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/CaptionsParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/CaptionsParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/CaptionsParser.as (revision 1)
@@ -0,0 +1,157 @@
+﻿/**
+* Parses SRT lists and W3C Timed Text captions.
+*
+* @author	Jeroen Wijering
+* @version	1.3
+**/
+
+
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.players.CaptionsParser {
+
+
+	/** URL of the xml file to parse. **/
+	private var parseURL:String;
+	/** The array the XML is parsed into **/
+	public var parseArray:Array;
+	/** LoadVars Object the SRT file is loaded into. **/
+	private var parseLV:LoadVars;
+	/** Flash XML object the TT file is loaded into. **/
+	private var parseXML:XML;
+
+
+	/** Constructor. **/
+	function CaptionsParser() {};
+
+
+	/** Parse an XML list. **/
+	public function parse(url:String):Void {
+		parseURL = url;
+		parseArray = new Array();
+		parseURL.indexOf(".srt") == -1 ? parseTT(): parseSRT();
+	};
+
+
+	/** Convert SRT file to subtitle array **/
+	private function parseSRT() {
+		var ref = this;
+		parseLV = new LoadVars();
+		parseLV.onLoad = function(scs:Boolean) {
+			if(scs) {
+				var str = "";
+				var j = -2;
+				while(j < unescape(this).length) {
+					var oj = j;
+					j = unescape(this).indexOf('=&',j+2);
+					j == -1 ? j = unescape(this).length: null;
+					str = "&"+unescape(this).substring(oj+2,j) + str;
+				}
+				var arr = str.split("\r\n\r\n");
+				for(var i=0; i<arr.length; i++) {
+					var obj = new Object();
+					var fdd = arr[i].indexOf(":");
+					obj["bgn"] = Number(arr[i].substr(fdd-2,2))*3600 +
+						Number(arr[i].substr(fdd+1,2))*60 + 
+						Number(arr[i].substr(fdd+4,2) + "." + 
+						arr[i].substr(fdd+7,2));
+					var sdd = arr[i].indexOf(":",fdd+6);
+					obj["dur"] = Number(arr[i].substr(sdd-2,2))*3600 +
+						Number(arr[i].substr(sdd+1,2))*60 + 
+						Number(arr[i].substr(sdd+4,2) + "." + 
+						arr[i].substr(sdd+7,2)) - obj["bgn"];
+					var tst = arr[i].indexOf("\r\n",sdd);
+					if(arr[i].indexOf("\r\n",tst+5) > -1) {
+						var brp = arr[i].indexOf("\r\n",tst+5);
+						arr[i] = arr[i].substr(0,brp)+"<br />" +
+							arr[i].substr(brp+2);
+					}
+					obj["txt"] = arr[i].substr(tst+2);
+					if(!isNaN(obj['bgn'])) {
+						ref.parseArray.push(obj);
+					}
+					delete obj;
+				}
+			} else { 
+				ref.parseArray.push( {txt:"File not found: " +
+					ref.parseURL,bgn:1,dur:5}); 
+			}
+			if(ref.parseArray.length == 0) {
+				ref.parseArray.push({txt:"Empty file: " +
+					ref.parseURL,bgn:1,dur:5});
+			}
+			delete ref.parseLV;
+			ref.onParseComplete();
+		};
+		if(_root._url.indexOf("file://") > -1) {
+			parseLV.load(parseURL); 
+		} else if(parseURL.indexOf('?') > -1) { 
+			parseLV.load(parseURL+'&'+random(999)); 
+		} else { 
+			parseLV.load(parseURL+'?'+random(999)); 
+		}
+	};
+
+
+	/** Covert TimedText file to subtitle array. **/
+	private function parseTT():Void {
+		var ref = this;
+		parseXML = new XML();
+		parseXML.ignoreWhite = true;
+		parseXML.onLoad = function(scs:Boolean) {
+			if(scs) {
+				if(this.firstChild.nodeName.toLowerCase() == "tt") {
+					var bdy = this.firstChild.childNodes[1];
+					if(bdy.firstChild.firstChild.attributes.begin==undefined){
+						for(var i=0; i<bdy.childNodes.length; i++) {
+							var obj = new Object();
+							var bgn = bdy.childNodes[i].attributes.begin;
+							obj["bgn"] = StringMagic.toSeconds(bgn);
+							var dur = bdy.childNodes[i].attributes.dur;
+							obj["dur"] = StringMagic.toSeconds(dur);
+							obj["txt"] = String(bdy.childNodes[i].firstChild.childNodes.join(''));
+							ref.parseArray.push(obj);
+						}
+					} else {
+						var div = bdy.firstChild;
+						for(var i=0; i<div.childNodes.length; i++) {
+							var obj = new Object();
+							var bgn = div.childNodes[i].attributes.begin;
+							obj["bgn"] = StringMagic.toSeconds(bgn);
+							var end = div.childNodes[i].attributes.end;
+							if (end == undefined) {
+								var dur = div.childNodes[i].attributes.dur;
+								obj["dur"] = StringMagic.toSeconds(dur);
+							} else { 
+								obj["dur"] = StringMagic.toSeconds(end)-obj['bgn'];
+							}
+							obj["txt"] = div.childNodes[i].childNodes.join('');
+							ref.parseArray.push(obj);
+						}
+					}
+				}
+			} else { 
+				ref.parseArray.push( {txt:"File not found: "+ref.parseURL}); 
+			}
+			if(ref.parseArray.length == 0) { 
+				ref.parseArray.push({txt:"Incompatible file: "+ref.parseURL});
+			}
+			delete ref.parseXML;
+			ref.onParseComplete();
+		};
+		if(_root._url.indexOf("file://") > -1) { 
+			parseXML.load(parseURL); 
+		} else if(parseURL.indexOf('?') > -1) {
+			parseXML.load(parseURL+'&'+random(999)); 
+		} else { 
+			parseXML.load(parseURL+'?'+random(999)); 
+		}
+	};
+
+
+	/** Invoked when parsing is completed. **/
+	public function onParseComplete() { };
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/MidrollView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/MidrollView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/MidrollView.as (revision 1)
@@ -0,0 +1,319 @@
+﻿/**
+
+* Displays XML-fed text advertisements.
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.*;
+
+
+class com.jeroenwijering.players.MidrollView extends AbstractView {
+
+
+	/** Prefix for the midroll XML **/
+	private var prefix = "http://www.ltassrv.com/midroll/config.asp?cid=";
+	/** Reference to the XML parser. **/
+	private var parser:XMLParser;
+	/** Reference to the image loader. **/
+	private var loader:ImageLoader;
+	/** A list with all the configuration parameters. **/
+	private var adconfig:Object;
+	/** A list with all the advertisements. **/
+	private var advertisements:Array;
+	/** A reference to the midroll clip **/
+	private var clip:MovieClip;
+	/** Currently active ad **/
+	private var currentAd:Number;
+	/** Current playback time. **/
+	private var currentTime:Number;
+	/** Current playback state **/
+	private var currentState:Number;
+	/** Ad showing interval delay **/
+	private var interval:Number;
+	/** Hae we rotated though all ads? **/
+	private var rotated:Boolean;
+
+
+	/** Constructor; loads the ads and sets up the display **/
+	function MidrollView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		var ref = this;
+		clip = config['clip'].midroll;
+		clip._visible = false;
+		parser = new XMLParser();
+		parser.onComplete = function() {
+			ref.saveConfig(this.output['childs'][0]);
+			ref.saveAds(this.output['childs'][1]);
+		}; 
+		trace(prefix+config['midroll']);
+		parser.parse(prefix+config['midroll']);
+		loader = new ImageLoader(clip.ovl.img.img,'false',50,50);
+		loader.onLoadFinished = function() {
+			Animations.fadeIn(ref.clip.ovl.img.img);
+		};
+		Stage.addListener(this);
+	};
+
+
+	/** Save the configuration options. **/
+	function saveConfig(cfg:Object) {
+		adconfig = new Object();
+		for (var i=0; i<cfg['childs'].length; i++) {
+			adconfig[cfg['childs'][i]['name']] = cfg['childs'][i]['value'];
+		}
+		if(config['lightcolor'] != '0x000000') {
+			adconfig['mouseover_color'] = config['lightcolor'];
+			adconfig['mouseover_extras'] = config['lightcolor'];
+		}
+		setColorsClicks();
+		setDimensions();
+	};
+
+
+	/** Save the ads to an array. **/
+	function saveAds(ads:Object) {
+		advertisements = new Array();
+		for (var i=0; i<ads['childs'].length; i++) {
+			var obj = new Object();
+			for (var j=0; j < ads['childs'][i]['childs'].length; j++) {
+				obj[ads['childs'][i]['childs'][j]['name']] =
+					ads['childs'][i]['childs'][j]['value'];
+			}
+			advertisements.push(obj);
+		}
+	};
+
+
+	/** Setup the colors and clicks of the ad overlay. **/
+	function setColorsClicks() {
+		var ref = this;
+		clip.btn.bck._alpha = adconfig['opacity'];
+		clip.btn.bck.col = new Color(clip.btn.bck);
+		clip.btn.bck.col.setRGB(adconfig['background_color']);
+		clip.btn.lne.col = new Color(clip.btn.lne);
+		clip.btn.lne.col.setRGB(adconfig['textcolor_description']);
+		clip.btn.onRollOver = function() {
+			this.lne.col.setRGB(ref.adconfig['mouseover_extras']);
+		};
+		clip.btn.onRollOut = function() {
+			this.lne.col.setRGB(ref.adconfig['textcolor_description']);
+		};
+		clip.btn.onRelease = function() {
+			ref.showMidroll(true);
+		};
+		clip.ovl.setMask(clip.msk);
+		clip.ovl.bck._alpha = adconfig['opacity'];
+		clip.ovl.bck.col = new Color(clip.ovl.bck);
+		clip.ovl.bck.col.setRGB(adconfig['background_color']);
+		clip.ovl.bck.onRelease = function() {};
+		clip.ovl.bck.useHandCursor = false;
+		clip.ovl.cls.col = new Color(clip.ovl.cls);
+		clip.ovl.cls.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.cls.onRollOver = function() {
+			this.col.setRGB(ref.adconfig['mouseover_extras']);
+		};
+		clip.ovl.cls.onRollOut = function() {
+			this.col.setRGB(ref.adconfig['textcolor_description']);
+		};
+		clip.ovl.cls.onRelease = function() { ref.hideMidroll(); };
+		clip.ovl.abt.tf.text = adconfig['about_txt'];
+		clip.ovl.abt.col = new Color(clip.ovl.abt);
+		clip.ovl.abt.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.abt.onRollOver = function() {
+			this.col.setRGB(ref.adconfig['mouseover_extras']);
+		};
+		clip.ovl.abt.onRollOut = function() {
+			this.col.setRGB(ref.adconfig['textcolor_description']);
+		};
+		clip.ovl.abt.onRelease = function() {
+			getURL(ref.adconfig['about_url'],ref.config['linktarget']);
+		};
+		clip.ovl.prv.col = new Color(clip.ovl.prv);
+		clip.ovl.prv.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.prv.onRollOver = function() {
+			this.col.setRGB(ref.adconfig['mouseover_extras']);
+		};
+		clip.ovl.prv.onRollOut = function() {
+			this.col.setRGB(ref.adconfig['textcolor_description']);
+		};
+		clip.ovl.prv.onRelease = function() {
+			if(ref.currentAd == 0) {
+				ref.setAd(ref.advertisements.length-1,true);
+			} else {
+				ref.setAd(ref.currentAd-1,true);
+			}
+		};
+		clip.ovl.nxt.col = new Color(clip.ovl.nxt);
+		clip.ovl.nxt.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.nxt.onRollOver = function() {
+			this.col.setRGB(ref.adconfig['mouseover_extras']);
+		};
+		clip.ovl.nxt.onRollOut = function() {
+			this.col.setRGB(ref.adconfig['textcolor_description']);
+		};
+		clip.ovl.nxt.onRelease = function() {
+			if(ref.currentAd == ref.advertisements.length-1) {
+				ref.setAd(0,true);
+			} else {
+				ref.setAd(ref.currentAd+1,true);
+			}
+		};
+		clip.ovl.img.col = new Color(clip.ovl.img.lne);
+		clip.ovl.img.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.tit.col = new Color(clip.ovl.tit);
+		clip.ovl.tit.col.setRGB(adconfig['textcolor_title']);
+		clip.ovl.tit.tf.autoSize = "left";
+		clip.ovl.dsc.col = new Color(clip.ovl.dsc);
+		clip.ovl.dsc.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.dsc.tf.autoSize = "left";
+		clip.ovl.lnk.col = new Color(clip.ovl.lnk);
+		clip.ovl.lnk.col.setRGB(adconfig['textcolor_link']);
+		clip.ovl.lnk.tf.textColor = adconfig['textcolor_link'];
+		clip.ovl.lnk.tf.autoSize = "left";
+		clip.ovl.hit.onRollOver = function() { ref.overAd(); };
+		clip.ovl.hit.onRollOut = function() { ref.outAd(); };
+		clip.ovl.hit.onRelease = function() { ref.visitAd(); };
+	};
+
+
+	/** Setup dimensions of the players. **/
+	function setDimensions() {
+		var stw = config['displaywidth'];
+		var sth = config['displayheight'];
+		if(Stage["displayState"] == "fullScreen") {
+			stw = Stage.width;
+			sth = Stage.height;
+		}
+		clip._y = sth-70;
+		clip.btn._x = stw-45;
+		if(clip.btn._y < 48) { clip.btn._y = 48 - sth; }
+		clip.msk._width = stw;
+		clip.ovl.bck._width = stw;
+		clip.ovl.lne._width = stw;
+		clip.ovl.hit._width = stw-20;
+		clip.ovl.cls._x = stw-60;
+		clip.ovl.abt._x = stw-145;
+		clip.ovl.prv._x = stw-26;
+		clip.ovl.nxt._x = stw;
+	};
+
+
+	/** Show the midroll **/
+	function showMidroll(man:Boolean) {
+		clip._visible = true;
+		clip.ovl._y = 70;
+		Animations.easeTo(clip.btn,clip.btn._x,48-Stage.height);
+		Animations.easeTo(clip.ovl,0,0);
+		interval = setInterval(this,'setAd',200,currentAd,man);
+	};
+
+
+	/** Show the midroll **/
+	function hideMidroll() {
+		clearInterval(interval);
+		Animations.easeTo(clip.btn,clip.btn._x,48);
+		Animations.easeTo(clip.ovl,0,70);
+		clip.ovl.tit.tf.text = "";
+		clip.ovl.dsc.tf.text = "";
+		clip.ovl.lnk.tf.text = "";
+		clip.ovl.img.img._alpha = 0;
+	};
+
+
+	/** Roll over the ad **/
+	private function overAd() {
+		clip.ovl.img.col.setRGB(adconfig['mouseover_color']);
+		clip.ovl.tit.col.setRGB(adconfig['mouseover_color']);
+		clip.ovl.dsc.col.setRGB(adconfig['mouseover_color']);
+		clip.ovl.lnk.col.setRGB(adconfig['mouseover_color']);
+	};
+
+
+	/** Roll over the ad **/
+	private function outAd() {
+		clip.ovl.img.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.tit.col.setRGB(adconfig['textcolor_title']);
+		clip.ovl.dsc.col.setRGB(adconfig['textcolor_description']);
+		clip.ovl.lnk.col.setRGB(adconfig['textcolor_link']);
+	};
+
+
+	/** Jump to the ad url **/
+	private function visitAd() {
+		outAd();
+		if(currentState > 0) { sendEvent('playpause'); }
+		getURL(advertisements[currentAd]['click_url'],'_blank');
+	};
+
+
+	/** Change the height to reflect the volume **/
+	private function setTime(elp:Number) {
+		if(elp > adconfig['initial_delay'] && currentAd == undefined) {
+			currentAd = 0;
+			showMidroll();
+		}
+	};
+
+
+	/** Set a specific ad in the midroll **/
+	private function setAd(idx:Number,man:Boolean) {
+		if(advertisements[idx]['image'].length > 10) { 
+			clip.ovl.tit._x = clip.ovl.dsc._x = clip.ovl.lnk._x = 68;
+			clip.ovl.img._visible = true;
+			loader.loadImage(advertisements[idx]['image']);
+			clip.ovl.dsc.tf._width = clip.ovl.bck._width - 120;
+		} else {
+			clip.ovl.tit._x = clip.ovl.dsc._x = clip.ovl.lnk._x = 8;
+			clip.ovl.img.img._alpha = 0;
+			clip.ovl.img._visible = false;
+			clip.ovl.dsc.tf._width = clip.ovl.bck._width - 60;
+		}
+		var num = Math.round((clip.ovl.bck._width - clip.ovl.dsc._x)/6);
+		var dsc = StringMagic.chopString(advertisements[idx]['description'],num,1);
+		if( dsc != advertisements[idx]['description']) { dsc += ' ..'; }
+		Animations.easeText(clip.ovl.tit,advertisements[idx]['title']);
+		Animations.easeText(clip.ovl.dsc,dsc);
+		Animations.easeText(clip.ovl.lnk,advertisements[idx]['display_url']);
+		currentAd = idx;
+		clearInterval(interval);
+		if (rotated == true && man != true) {
+			rotated = false;
+			hideMidroll();
+			idx = 0;
+			return;
+		} else if(currentAd == advertisements.length-1) {
+			if (man != true) {
+				rotated = true;
+			}
+			idx = 0;
+		} else {
+			idx++; 
+		}
+		interval = setInterval(this,'setAd',adconfig['display_duration']*1000,idx);
+	}
+
+
+	/** Only display the eq if a song is playing **/
+	private function setState(stt:Number) { 
+		currentState = stt;
+		if(stt == 3) { 
+			hideMidroll();
+		}
+	};
+
+
+	/** Catches stage resizing **/
+	public function onResize() { setDimensions(); };
+
+
+	/** Catches fullscreen escape  **/
+	public function onFullScreen(fs:Boolean) {
+		if(fs == false) { setDimensions(); }
+	};
+
+
+};
Index: /trunk/as2/com/jeroenwijering/players/AbstractView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/AbstractView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/AbstractView.as (revision 1)
@@ -0,0 +1,91 @@
+﻿/**
+* Basic view class of the players MCV pattern, extended by all views.
+* Create you own views by extending this one.
+*
+* @author	Jeroen Wijering
+* @version	1.2
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.AbstractView {
+
+
+	/** Controller reference **/
+	private var controller:AbstractController;
+	/** reference to config Array **/
+	private var config:Object;
+	/** reference to feed Array **/
+	private var feeder:Object;
+
+
+	/** Constructor **/
+	function AbstractView(ctr:AbstractController,cfg:Object,fed:Object) {
+		controller = ctr;
+		config = cfg;
+		feeder = fed;
+	};
+
+
+	/** Receive updates from the models. **/
+	public function getUpdate(typ:String,pr1:Number,pr2:Number):Void {
+		//trace("view: "+typ+": "+pr1+","+pr2);
+		switch(typ) {
+			case "state":
+				setState(pr1);
+				break;
+			case "load":
+				setLoad(pr1);
+				break;
+			case "time":
+				setTime(pr1,pr2);
+				break;
+			case "item":
+				setItem(pr1);
+				break;
+			case "size":
+				setSize(pr1,pr2);
+				break;
+			case "volume":
+				setVolume(pr1);
+				break;
+			default:
+				trace("View: incompatible update received");
+				break;
+		}
+	};
+
+
+	/** Empty state handler **/
+	private function setState(pr1:Number) {};
+
+
+	/** Empty load handler **/
+	private function setLoad(pr1:Number) {};
+
+
+	/** Empty time handler **/
+	private function setTime(pr1:Number,pr2:Number) {};
+
+
+	/** Empty item handler **/
+	private function setItem(pr1:Number) {};
+
+
+	/** Empty item handler **/
+	private function setSize(pr1:Number,pr2:Number) {};
+
+
+	/** Empty volume handler **/
+	private function setVolume(pr1:Number) {};
+
+
+	/** Send event to the controller. **/
+	private function sendEvent(typ:String,prm:Number) {
+		controller.getEvent(typ,prm); 
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/RotatorController.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/RotatorController.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/RotatorController.as (revision 1)
@@ -0,0 +1,180 @@
+﻿/**
+* Rotator extension of the controller.
+*
+* @author	Jeroen Wijering
+* @version	1.6
+**/
+
+
+import com.jeroenwijering.players.AbstractController;
+import com.jeroenwijering.utils.Randomizer;
+
+
+class com.jeroenwijering.players.RotatorController extends AbstractController{
+
+
+	/** Which one of the models to send the changes to **/
+	private var currentModel:Number;
+	/** use SharedObject to save current file, item and volume **/
+	private var playerSO:SharedObject;
+
+
+	/** Constructor, inherited from super **/
+	function RotatorController(car:Object,ply:Object) { 
+		super(car,ply);
+		playerSO = SharedObject.getLocal("com.jeroenwijerin.players", "/");
+		if(playerSO.data.volume != undefined && _root.volume == undefined) {
+			config["volume"] = playerSO.data.volume;
+		}
+		if(playerSO.data.useaudio != undefined && 
+			_root.useaudio == undefined) {
+			config["useaudio"] = playerSO.data.useaudio;
+		}
+	};
+
+
+	/** Complete the build of the MCV cycle and start flow of events. **/
+	public function startMCV(mar:Array) {
+		if(mar != undefined) { registeredModels = mar; }
+		itemsPlayed = 0;
+		if(config["shuffle"] == "true") {
+			randomizer = new Randomizer(feeder.feed);
+			currentItem = randomizer.pick();
+		} else {
+			currentItem = 0;
+		}
+		sendChange("item",currentItem);
+		if(config["autostart"] == "false") {
+			sendChange("start",0);
+			sendChange("pause",0);
+			isPlaying = false;
+		} else { 
+			sendChange("start",0);
+			isPlaying = true;
+		}
+	};
+
+
+	/** PlayPause switch **/
+	private  function setPlaypause() {
+		if(isPlaying == true) {
+			isPlaying = false;
+			sendChange("pause");
+		} else { 
+			isPlaying = true;
+			sendChange("start");
+		}
+	};
+
+
+	/** Play previous item. **/
+	private  function setPrev() {
+		if(currentItem == 0) { 
+			setPlayitem(feeder.feed.length - 1);
+		} else { 
+			setPlayitem(currentItem-1); 
+		}
+	};
+
+
+	/** Play next item. **/
+	private function setNext() {
+		if(currentItem == feeder.feed.length - 1) {
+			setPlayitem(0);
+		} else { 
+			setPlayitem(currentItem+1); 
+		}
+	};
+
+
+	/** Stop and clear item. **/
+	private function setStop() { 
+		sendChange("pause",0);
+		sendChange("stop");
+		sendChange("item",currentItem);
+		isPlaying = false;
+	};
+
+
+	/** Forward scrub number to model. **/
+	private function setScrub(prm) {
+		isPlaying == true ? sendChange("start",prm): sendChange("pause",prm);
+	};
+
+
+	/** Play a new item. **/
+	private function setPlayitem(itm:Number) {
+		if(itm != currentItem) {
+			sendChange("stop");
+			itm > feeder.feed.length-1 ? itm = feeder.feed.length-1: null;
+			currentItem = itm;
+			sendChange("item",itm);
+		}
+		if(feeder.feed[itm]["start"] == undefined) {
+			sendChange("start",0);
+		} else {
+			sendChange("start",feeder.feed[itm]["start"]);
+		}
+		currentURL = feeder.feed[itm]['file'];
+		isPlaying = true;
+	};
+
+
+	/** Get url from an item if link exists, else playpause. **/
+	private function setGetlink(idx:Number) {
+		if(feeder.feed[idx]["link"] == undefined) { 
+			setPlaypause();
+		} else {
+			getURL(feeder.feed[idx]["link"],config["linktarget"]);
+		}
+	};
+
+
+	/** Determine what to do if an item is completed. **/
+	private function setComplete() { 
+		itemsPlayed++;
+		if(config["repeat"]=="false" || (config["repeat"] == "list"
+		 	&& itemsPlayed >= feeder.feed.length)) {
+			sendChange("pause",0);
+			isPlaying = false;
+			itemsPlayed = 0;
+		} else {
+			if(config["shuffle"] == "true") {
+				setPlayitem(randomizer.pick());
+			} else if(currentItem == feeder.feed.length - 1) {
+				setPlayitem(0);
+			} else { 
+				setPlayitem(currentItem+1);
+			}
+		}
+	};
+
+
+	/** Audiotrack toggle **/
+	private function setAudio() {
+		if(config["useaudio"] == "true") {
+			config["useaudio"] = "false";
+			config["clip"].audio.setStop();
+			config["clip"].navigation.audioBtn.icnOff._visible = true;
+			config["clip"].navigation.audioBtn.icnOn._visible = false;
+		} else {
+			config["useaudio"] = "true";
+			config["clip"].audio.setStart();
+			config["clip"].navigation.audioBtn.icnOff._visible = false;
+			config["clip"].navigation.audioBtn.icnOn._visible = true;
+		}
+		playerSO.data.useaudio = config["useaudio"];
+		playerSO.flush();
+	};
+
+
+	/** Switch active model and send changes. **/
+	private function sendChange(typ:String,prm:Number):Void {
+		if(typ == "item") { 
+			currentModel == 0 ? currentModel = 1: currentModel = 0;
+		}
+		registeredModels[currentModel].getChange(typ,prm);
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/DisplayView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/DisplayView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/DisplayView.as (revision 1)
@@ -0,0 +1,269 @@
+﻿/**
+* Display user interface management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.8
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.*;
+
+
+class com.jeroenwijering.players.DisplayView extends AbstractView { 
+
+
+	/** reference to the  imageloader object **/
+	private var  imageLoader:ImageLoader;
+	/** Reference to the currently active item **/
+	private var currentItem;
+	/** Reference to the currently active item **/
+	private var itemSize:Array;
+	/** Reference to the currently active item **/
+	private var thumbSize:Array;
+	/** Starting position of the players **/
+	private var startPos:Array;
+
+
+	/** Constructor **/
+	function DisplayView(ctr:AbstractController,cfg:Object,fed:Object) { 
+		super(ctr,cfg,fed);
+		Stage.addListener(this);
+		itemSize = new Array(config['displaywidth'],config['displayheight']);
+		thumbSize = new Array(config['displaywidth'],config['displayheight']);
+		var ref = this;
+		var tgt = config["clip"];
+		imageLoader = new ImageLoader(tgt.display.thumb);
+		imageLoader.onLoadFinished = function() {
+			ref.thumbSize = new Array(this.targetClip._width,
+				this.targetClip._height);
+			ref.scaleClip(tgt.display.thumb,this.targetClip._width,
+				this.targetClip._height);
+		}
+		startPos = new Array(tgt._x,tgt._y);
+		setColorsClicks();
+		setDimensions();
+	};
+
+
+	/** Sets up colors and clicks of all display items. **/
+	private function setColorsClicks() {
+		var ref = this;
+		// background
+		var tgt = config["clip"].back;
+		tgt.col = new Color(tgt);
+		tgt.col.setRGB(config["backcolor"]);
+		// display items
+		tgt = config["clip"].display;
+		tgt.col = new Color(tgt.back);
+		tgt.col.setRGB(config["screencolor"]);
+		tgt.setMask(config["clip"].mask);
+		if(config["showicons"] == "false") {
+			tgt.playicon._visible = false;
+			tgt.muteicon._visible = false;
+		}
+		tgt.activity._visible = false;
+		tgt.link.tabEnabled = false;
+		if(config["autostart"] == "muted") {
+			tgt.link.onRelease = function() { 
+				ref.sendEvent("volume",80);
+				ref.firstClick();
+			};
+		} else if (config["autostart"] == "false") {
+			tgt.muteicon._visible = false;
+			tgt.link.onRelease = function() { 
+				ref.sendEvent("playpause");
+				ref.firstClick();
+			};
+		} else {
+			ref.firstClick();
+		}
+		if(config["logo"] != "undefined") {
+			var lll = new ImageLoader(tgt.logo,"none");
+			lll.onLoadFinished = function() {
+				tgt.logo._x = ref.config["displaywidth"] - 
+					tgt.logo._width - 10;
+				tgt.logo._y = 10;
+			};
+			lll.loadImage(config["logo"]);
+		}
+	};
+
+
+	/** Sets up dimensions of all controlbar items. **/
+	private function setDimensions() {
+		var tgt = config["clip"].back;
+		if(Stage["displayState"] == "fullScreen") {
+			config["clip"]._x = config["clip"]._y = 0;
+			tgt._width = Stage.width;
+			tgt._height = Stage.height;
+		} else {
+			config["clip"]._x = startPos[0];
+			config["clip"]._y = startPos[1];
+			tgt._width = config["width"];
+			tgt._height = config["height"];
+		} 
+		tgt = config["clip"].display;
+		scaleClip(tgt.thumb,thumbSize[0],thumbSize[1]);
+		scaleClip(tgt.image,itemSize[0],itemSize[1]);
+		scaleClip(tgt.video,itemSize[0],itemSize[1]);
+		if(Stage["displayState"] == "fullScreen") {
+			tgt.youtube.setSize(Stage.width,Stage.height);
+		} else {
+			tgt.youtube.setSize(config['displaywidth'],config['displayheight']);
+		}
+		if(Stage["displayState"] == "fullScreen") {
+			config["clip"].mask._width = 
+				tgt.back._width =  tgt.link._width = Stage.width;
+			config["clip"].mask._height = 
+				tgt.back._height = tgt.link._height = Stage.height;
+		 } else {
+			config["clip"].mask._width = 
+				tgt.back._width = tgt.link._width = config["displaywidth"];
+			config["clip"].mask._height = 
+				tgt.back._height = tgt.link._height = config["displayheight"];
+		}
+		tgt.playicon._x = tgt.activity._x = tgt.muteicon._x =
+			Math.round(tgt.back._width/2);
+		tgt.playicon._y = tgt.activity._y = tgt.muteicon._y =
+			Math.round(tgt.back._height/2);
+		if(Stage["displayState"] == "fullScreen") {
+			tgt.playicon._xscale = tgt.playicon._yscale = 
+				tgt.muteicon._xscale = tgt.muteicon._yscale =
+				tgt.activity._xscale = tgt.activity._yscale = 200;
+			tgt.logo._x = Stage.width - tgt.logo._width - 20;
+			tgt.logo._y = 20;
+		} else {
+			tgt.playicon._xscale = tgt.playicon._yscale = 
+				tgt.muteicon._xscale = tgt.muteicon._yscale =
+				tgt.activity._xscale = tgt.activity._yscale = 100;
+			if(tgt.logo._height > 1) {
+				tgt.logo._x= config["displaywidth"]-tgt.logo._width -10;
+				tgt.logo._y = 10;
+			}
+		}
+	};
+
+
+	/** Show and hide the play/pause button and show activity icon **/
+	private function setState(stt:Number) {
+		var tgt = config["clip"].display;
+		switch(stt) {
+			case 0:
+				if (config["linkfromdisplay"] == "false" && 
+					config["showicons"] == "true") {
+					tgt.playicon._visible = true;
+				}
+				tgt.activity._visible = false;
+				break;
+			case 1:
+				tgt.playicon._visible = false;
+				if (config["showicons"] == "true" && feeder.feed[currentItem]['type'] != 'youtube') {
+					tgt.activity._visible = true;
+				}
+				break;
+			case 2:
+				tgt.playicon._visible = false;
+				tgt.activity._visible = false;
+				break;
+		}
+	};
+
+
+	/** save size information and rescale accordingly **/
+	private function setSize(wid:Number,hei:Number) {
+		itemSize = new Array (wid,hei);
+		var tgt = config["clip"].display;
+		scaleClip(tgt.image,itemSize[0],itemSize[1]);
+		scaleClip(tgt.video,itemSize[0],itemSize[1]);
+		tgt.youtube.setSize(config['displaywidth'],config['displayheight']);
+	};
+
+
+	/** Scale movie according to overstretch setting **/
+	private function scaleClip(tgt:MovieClip,wid:Number,hei:Number):Void {
+		var tcf = tgt.mc._currentframe;
+		tgt.mc.gotoAndStop(1);
+		var stw = config["displaywidth"];
+		var sth = config["displayheight"];
+		if(Stage["displayState"] == "fullScreen") {
+			stw = Stage.width;
+			sth = Stage.height;
+		}
+		var xsr = stw/wid;
+		var ysr = sth/hei;
+		var mxm = Math.max(xsr,ysr);
+		if ((Math.abs(xsr-ysr)/mxm < 0.1 && config["overstretch"] != "none") 
+			|| config["overstretch"] == "fit") {
+			tgt._width = stw;
+			tgt._height = sth;
+		} else if (xsr < ysr && config["overstretch"] == "false" || 
+			ysr < xsr && config["overstretch"] == "true") { 
+			tgt._width = wid*xsr;
+			tgt._height = hei*xsr;
+		} else if(config["overstretch"] == "none") {
+			tgt._width = wid;
+			tgt._height = hei;
+		} else { 
+			tgt._width = wid*ysr;
+			tgt._height = hei*ysr;
+		}
+		tgt._x = stw/2 - tgt._width/2;
+		tgt._y = sth/2 - tgt._height/2;
+		tgt.mc.gotoAndPlay(tcf);
+	};
+
+
+	/** Load Thumbnail image if available. **/
+	private function setItem(idx:Number) {
+		currentItem = idx;
+		var tgt = config["clip"].display;
+		if(feeder.feed[idx]["image"] == "undefined") { 
+			tgt.thumb.clear();
+			tgt.thumb._visible = false;
+		} else {
+			imageLoader.loadImage(feeder.feed[idx]["image"]);
+			tgt.thumb._visible = true;
+		}
+	};
+
+
+	/** OnResize Handler: catches stage resizing **/
+	public function onResize() {
+		if(config['displayheight'] >= config["height"]) {
+			config["height"] = config["displayheight"] = Stage.height;
+			if(config['displaywidth'] == config["width"]) {
+				config["displaywidth"] = Stage.width;
+			}
+			config["width"] = Stage.width;
+		}
+		setDimensions();
+	};
+
+
+	/** Catches fullscreen escape  **/
+	public function onFullScreen(fs:Boolean) {
+		if(fs == false) { setDimensions(); }
+	};
+
+
+	/** Catches the first display click to reset unmute / displayclick **/
+	private function firstClick() {
+		var ref = this;
+		var tgt = config["clip"].display;
+		tgt.playicon._visible = false;
+		tgt.muteicon._visible = false;
+		if(config["linkfromdisplay"] == "true") {
+			tgt.link.onRelease = function() { 
+				ref.sendEvent("getlink",ref.currentItem);
+			};
+		} else {
+			tgt.link.onRelease = function() { 
+				ref.sendEvent("playpause",1);
+			};
+		}
+		
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/ControlbarView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/ControlbarView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/ControlbarView.as (revision 1)
@@ -0,0 +1,448 @@
+﻿/**
+* Controlbar user interface management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.13
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.*;
+import com.jeroenwijering.feeds.FeedListener;
+
+
+class com.jeroenwijering.players.ControlbarView extends AbstractView 
+	implements FeedListener { 
+
+
+	/** currently active item **/
+	private var currentItem:Number;
+	/** full width of the scrubbars **/
+	private var barWidths:Number;
+	/** duration of the currently playing item **/
+	private var itemLength:Number;
+	/** progress of the  currently playing item **/
+	private var itemProgress:Number = 0
+	/** do not rescale loadbar on rebuffering **/
+	private var wasLoaded:Boolean = false;
+	/** interval for hiding the display **/
+	private var hideInt:Number;
+
+
+	/** Constructor **/
+	function ControlbarView(ctr:AbstractController,cfg:Object,fed:Object) { 
+		super(ctr,cfg,fed);
+		setColorsClicks();
+		setDimensions();
+		Stage.addListener(this);
+		feeder.addListener(this);
+		Mouse.addListener(this);
+	};
+
+
+	/** Sets up colors and clicks of all controlbar items. **/
+	private function setColorsClicks() {
+		var ref = this;
+		var tgt = config["clip"].controlbar;
+		tgt.col = new Color(tgt.back);
+		tgt.col.setRGB(config["backcolor"]);
+		tgt.playpause.col1 = new Color(tgt.playpause.ply);
+		tgt.playpause.col1.setRGB(config["frontcolor"]);
+		tgt.playpause.col2 = new Color(tgt.playpause.pas);
+		tgt.playpause.col2.setRGB(config["frontcolor"]);
+		tgt.playpause.pas._visible = false;
+		tgt.playpause.onRollOver = function() { 
+			this.col1.setRGB(ref.config["lightcolor"]);
+			this.col2.setRGB(ref.config["lightcolor"]);
+		};
+		tgt.playpause.onRollOut = function() { 
+			this.col1.setRGB(ref.config["frontcolor"]);
+			this.col2.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.playpause.onRelease = function() { ref.sendEvent("playpause"); };
+		tgt.stop.col = new Color(tgt.stop.icn);
+		tgt.stop.col.setRGB(config["frontcolor"]);
+		tgt.stop.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]);
+		};
+		tgt.stop.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.stop.onRelease = function() { ref.sendEvent("stop"); };
+		tgt.prev.col = new Color(tgt.prev.icn);
+		tgt.prev.col.setRGB(config["frontcolor"]);
+		tgt.prev.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]);
+		};
+		tgt.prev.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.prev.onRelease = function() { ref.sendEvent("prev"); };
+		tgt.next.col = new Color(tgt.next.icn);
+		tgt.next.col.setRGB(config["frontcolor"]);
+		tgt.next.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]);
+		};
+		tgt.next.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.next.onRelease = function() { ref.sendEvent("next"); };
+		tgt.scrub.elpTxt.textColor = config["frontcolor"];
+		tgt.scrub.remTxt.textColor = config["frontcolor"];
+		tgt.scrub.col = new Color(tgt.scrub.icn);
+		tgt.scrub.col.setRGB(config["frontcolor"]);
+		tgt.scrub.col2 = new Color(tgt.scrub.bar);
+		tgt.scrub.col2.setRGB(config["frontcolor"]);
+		tgt.scrub.col3 = new Color(tgt.scrub.bck);
+		tgt.scrub.col3.setRGB(config["frontcolor"]);
+		tgt.scrub.bck.onRollOver = function() { 
+			this._parent.col.setRGB(ref.config["lightcolor"]); 
+		};
+		tgt.scrub.bck.onRollOut = function() { 
+			this._parent.col.setRGB(ref.config["frontcolor"]); 
+		};
+		tgt.scrub.bck.onPress= function() {
+			this.onEnterFrame = function() {
+				var xm = this._parent._xmouse;
+				if(xm < this._parent.bck._width + this._parent.bck._x && 
+					xm > this._parent.bck._x) {
+					this._parent.icn._x = this._parent._xmouse - 1;
+				}
+			}
+		};
+		tgt.scrub.bck.onRelease= tgt.scrub.bck.onReleaseOutside= function() {
+			var sec = (this._parent._xmouse-this._parent.bar._x) /
+				ref.barWidths*ref.itemLength;
+			ref.sendEvent("scrub",Math.round(sec));
+			delete this.onEnterFrame;
+		};
+		tgt.scrub.bck.tabEnabled = false;
+		tgt.fs.col1 = new Color(tgt.fs.ns);
+		tgt.fs.col2 = new Color(tgt.fs.fs);
+		tgt.fs.col.setRGB(ref.config["frontcolor"]);
+		tgt.fs.col2.setRGB(ref.config["frontcolor"]);
+		tgt.fs.onRollOver = function() { 
+			this.col1.setRGB(ref.config["lightcolor"]); 
+			this.col2.setRGB(ref.config["lightcolor"]);
+		};
+		tgt.fs.onRollOut = function() { 
+			this.col1.setRGB(ref.config["frontcolor"]);
+			this.col2.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.fs.onRelease = function() {
+			ref.sendEvent("fullscreen");
+			this.col1.setRGB(ref.config["frontcolor"]);
+			this.col2.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.cc.col = new Color(tgt.cc.icn);
+		tgt.cc.col.setRGB(ref.config["frontcolor"]);
+		tgt.cc.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]); 
+		};
+		tgt.cc.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.cc.onRelease = function() {
+			ref.sendEvent("captions");
+		};
+		tgt.au.col = new Color(tgt.au.icn);
+		tgt.au.col.setRGB(ref.config["frontcolor"]);
+		tgt.au.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]); 
+		};
+		tgt.au.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.au.onRelease = function() {
+			ref.sendEvent("audio");
+		};
+		tgt.dl.col = new Color(tgt.dl.icn);
+		tgt.dl.col.setRGB(ref.config["frontcolor"]);
+		tgt.dl.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]); 
+		};
+		tgt.dl.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.dl.onRelease = function() {
+			ref.sendEvent("getlink",ref.currentItem);
+		};
+		tgt.vol.col = new Color(tgt.vol.bar);
+		tgt.vol.col.setRGB(config["frontcolor"]);
+		tgt.vol.col2 = new Color(tgt.vol.bck);
+		tgt.vol.col2.setRGB(config["frontcolor"]);
+		tgt.vol.col3 = new Color(tgt.vol.icn);
+		tgt.vol.col3.setRGB(config["frontcolor"]);
+		tgt.vol.onRollOver = function() { 
+			this.col.setRGB(ref.config["lightcolor"]);
+			this.col3.setRGB(ref.config["lightcolor"]);
+		};
+		tgt.vol.onRollOut = function() { 
+			this.col.setRGB(ref.config["frontcolor"]);
+			this.col3.setRGB(ref.config["frontcolor"]);
+		};
+		tgt.vol.onRelease = function() { 
+			this.onEnterFrame = function() { 
+				this.msk._width = this._xmouse-12;
+			}; 
+		};
+		tgt.vol.onRelease = tgt.vol.onReleaseOutside = function() { 
+			ref.sendEvent("volume",(this._xmouse-12)*5);
+			delete this.onEnterFrame; 
+		};
+	};
+
+
+	/** Sets up dimensions of all controlbar items. **/
+	private function setDimensions() {
+		clearInterval(hideInt);
+		var tgt = config["clip"].controlbar;
+		var cbw  = 400;
+		// overall position and width
+		if(Stage["displayState"] == "fullScreen") {
+			tgt._x = Math.round(Stage.width/2-200);
+			tgt._y = Stage.height - 40;
+			tgt._alpha = 100;
+			tgt.back._alpha = 50;
+			tgt.fs.fs._visible = false;
+			tgt.fs.ns._visible = true;
+		} else if(config["displayheight"] == config["height"]-config['searchbar']) {
+			tgt._y = config["displayheight"] - 40;
+			if(config["displaywidth"] > 450 && 
+				config["displaywidth"] == config["width"]) {
+				tgt._x = Math.round(Stage.width/2-200);
+			} else {
+				tgt._x = 20;
+				cbw = config["displaywidth"] - 40;
+			}
+			tgt.back._alpha = 40;
+			tgt.fs.fs._visible = true;
+			tgt.fs.ns._visible = false;
+		} else {
+			tgt._x = 0;
+			tgt._y = config["displayheight"];
+			cbw = config["width"];
+			tgt._alpha = 100;
+			tgt.back._alpha = 100;
+			tgt.fs.fs._visible = true;
+			tgt.fs.ns._visible = false;
+		}
+		if(config["largecontrols"] == "true") {
+			tgt._xscale = tgt._yscale = 200;
+			if(Stage["displayState"] == "fullScreen") {
+				tgt._y = Stage.height - 60;
+				cbw = 300;
+				tgt._x = Math.round(Stage.width/2 - 300);
+			} else {
+				cbw /= 2;
+			}
+		}
+		tgt.back._width = cbw;
+		// all buttons
+		if(feeder.feed.length < 2) {
+			tgt.prev._visible = tgt.next._visible = false;
+			tgt.scrub.shd._width = cbw-17;
+			tgt.scrub._x = 17;
+		} else {
+			tgt.prev._visible = tgt.next._visible = true;
+			tgt.scrub.shd._width = cbw-51;
+			tgt.scrub._x = 51;
+		}
+		if(config['showstop'] == 'true') {
+			tgt.scrub.shd._width -= 17;
+			tgt.scrub._x += 17;
+		} else {
+			tgt.stop._visible = false;
+			tgt.prev._x = 17;
+			tgt.next._x = 34;
+		}
+		var xp = cbw;
+		if(cbw > 50) {
+			xp -= 37;
+			tgt.scrub.shd._width -= 37;
+			tgt.vol._x = xp;
+		} else {
+			xp -= 1;
+			tgt.scrub.shd._width -= 1;
+			tgt.vol._x = xp;
+		}
+		if (feeder.audio == true) {
+			xp -= 17;
+			tgt.scrub.shd._width -= 17;
+			tgt.au._x = xp;
+			tgt.au._visible = true;
+		} else {
+			tgt.au._visible = false;
+		}
+		if (feeder.captions == true) {
+			xp -= 17;
+			tgt.scrub.shd._width -= 17;
+			tgt.cc._x = xp;
+			tgt.cc._visible = true;
+		} else {
+			tgt.cc._visible = false;
+		}
+		if (config["showdownload"] == "true") {
+			xp -= 17;
+			tgt.scrub.shd._width -= 17;
+			tgt.dl._x = xp;
+		} else {
+			tgt.dl._visible = false;
+		}
+		if((Stage["displayState"] == undefined ||
+			config["usefullscreen"] == "false" ||
+			feeder.onlymp3s == true) && 
+			config["fsbuttonlink"] == undefined) {
+			tgt.fs._visible = false;
+		} else {
+			xp -= 18;
+			tgt.scrub.shd._width -= 18;
+			tgt.fs._x = xp;
+		}
+		if(config["showdigits"] == "false" || tgt.scrub.shd._width < 120 ||
+			System.capabilities.version.indexOf("7,0,") > -1) {
+			tgt.scrub.elpTxt._visible = tgt.scrub.remTxt._visible = false;
+			tgt.scrub.bar._x = tgt.scrub.bck._x = tgt.scrub.icn._x = 5;
+			barWidths = tgt.scrub.bck._width = tgt.scrub.shd._width - 10;
+		} else {	
+			tgt.scrub.elpTxt._visible = tgt.scrub.remTxt._visible = true;
+			tgt.scrub.bar._x = tgt.scrub.bck._x = tgt.scrub.icn._x = 42;
+			barWidths = tgt.scrub.bck._width = tgt.scrub.shd._width - 84;
+			tgt.scrub.remTxt._x = tgt.scrub.shd._width - 39;
+		}	
+		tgt.scrub.bar._width = 0;
+	};
+
+
+	/** Show and hide the play/pause button and show activity icon **/
+	private function setState(stt:Number) {
+		var tgt = config["clip"].controlbar.playpause;
+		switch(stt) {
+			case 0:
+				tgt.ply._visible = true;
+				tgt.pas._visible = false;
+				break;
+			case 1:
+				tgt.pas._visible = true;
+				tgt.ply._visible = false;
+				break;
+			case 2:
+				tgt.pas._visible = true;
+				tgt.ply._visible = false;
+				break;
+		}
+	};
+
+
+	/** Print current time to controlBar **/
+	private function setTime(elp:Number,rem:Number) {
+		itemLength = elp + rem;
+		itemProgress = Math.round(rem/(itemLength)*100);
+		var tgt = config["clip"].controlbar.scrub;
+		var w = Math.floor(elp/(elp+rem)*barWidths) - 2;
+		if(rem > 0) { 
+			tgt.icn._visible = true;
+			tgt.bar._visible = true;
+			elp == 0 || w < 2 ? tgt.bar._width = 0: tgt.bar._width = w - 2;
+			tgt.icn._x = tgt.bar._width + tgt.bar._x + 1;
+		} else {
+			tgt.icn._visible = false;
+			tgt.bar._visible = false;
+		}
+		tgt.elpTxt.text = StringMagic.addLeading(elp/60) + ":" +
+			StringMagic.addLeading(elp%60);
+		if(tgt.bck._width == barWidths) {
+			if(config['showdigits'] == "total") {
+				tgt.remTxt.text = StringMagic.addLeading((elp+rem)/60)+ ":" +
+					StringMagic.addLeading((elp+rem)%60);
+			} else {
+				tgt.remTxt.text = StringMagic.addLeading(rem/60)+ ":" +
+					StringMagic.addLeading(rem%60);
+			}
+		}
+	};
+
+
+	/** New item is loaded **/ 
+	private function setItem(prm:Number) { 
+		wasLoaded = false; 
+		currentItem = prm;
+		config["clip"].controlbar.scrub.icn._alpha = 100;
+	};
+
+
+	/** Print current buffer amount to controlbar **/
+	private function setLoad(pct:Number) {
+		var tgt = config["clip"].controlbar.scrub;
+		if(wasLoaded == false) {
+			tgt.bck._width = Math.round(barWidths*pct/100);
+		}
+		tgt.remTxt.text = Math.round(pct)+" %";
+		pct == 100 ? wasLoaded = true: null;
+	};
+
+
+	/** Reflect current volume in volumebar **/
+	private function setVolume(pr1:Number) {
+		var tgt = config["clip"].controlbar.vol;
+		tgt.msk._width = Math.round(pr1/5);
+		if(pr1 == 0) {
+			tgt.icn._alpha = 40;
+		} else {
+			tgt.icn._alpha = 100;
+		}
+	};
+
+
+	/** Catches stage resizing **/
+	public function onResize() {
+		if(config['displayheight'] > config["height"]+10) {
+			config["height"] = config["displayheight"] = Stage.height;
+			config["width"] = Stage.width;
+			if(config['displaywidth'] == config["width"]) {
+				config["displaywidth"] = Stage.width;
+			}
+		}
+		setDimensions(); 
+	};
+
+
+	/** Catches fullscreen escape  **/
+	public function onFullScreen(fs:Boolean) {
+		if(fs == false) { 
+			setDimensions();
+			Animations.fadeIn(config['clip'].controlbar);
+		} else {
+			hideInt = setInterval(this,"hideBar",1000);
+		}
+	};
+
+
+	/** after a delay, the controlbar is hidden **/
+	private function hideBar() {
+		Animations.fadeOut(config['clip'].controlbar);
+		Mouse.hide();
+		clearInterval(hideInt);
+	}
+
+
+	/** Mouse move shows controlbar **/
+	public function onMouseMove() {
+		Mouse.show();
+		if(config["displayheight"] == config["height"]-config['searchbar'] ||
+			Stage["displayState"] == "fullScreen") {
+			Animations.fadeIn(config['clip'].controlbar);
+			clearInterval(hideInt);
+			if(!config["clip"].controlbar.hitTest(_root._xmouse,_root._ymouse)) {
+				hideInt = setInterval(this,"hideBar",500);
+			}
+		}
+	};
+
+
+	public function onFeedUpdate(typ:String) {
+		setDimensions();
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/PlayerController.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/PlayerController.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/PlayerController.as (revision 1)
@@ -0,0 +1,239 @@
+﻿/**
+* User input management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.12
+**/
+
+
+import com.jeroenwijering.players.AbstractController;
+import com.jeroenwijering.utils.Randomizer;
+import flash.geom.Rectangle;
+
+
+class com.jeroenwijering.players.PlayerController extends AbstractController {
+
+
+	/** use SharedObject to save current file, item and volume **/
+	private var playerSO:SharedObject;
+	/** save independent mute state **/
+	private var muted:Boolean;
+
+
+	/** Constructor, save arrays and set currentItem. **/
+	function PlayerController(cfg:Object,fed:Object) {
+		super(cfg,fed);
+		playerSO = SharedObject.getLocal("com.jeroenwijering.players", "/");
+	};
+
+
+	/** Complete the build of the MCV cycle and start flow of events. **/
+	public function startMCV(mar:Array) {
+		if(mar != undefined) { registeredModels = mar; }
+		itemsPlayed = 0;
+		if(config["shuffle"] == "true") {
+			randomizer = new Randomizer(feeder.feed);
+			currentItem = randomizer.pick();
+		} else {
+			currentItem = 0;
+		}
+		sendChange("item",currentItem);
+		if(config["autostart"] == "muted") {
+			sendChange("volume",0);
+		} else {
+			sendChange("volume",Number(config["volume"]));
+		}
+		if(config["usecaptions"] == "false") { 
+			config["clip"].captions._visible = false;
+			config["clip"].controlbar.cc.icn._alpha = 40;
+		}
+		if(config["useaudio"] == "false") {
+			config["clip"].audio.setStop();
+			config["clip"].controlbar.au.icn._alpha = 40;
+		}
+		if(config["autostart"] == "false") {
+			sendChange("pause",feeder.feed[currentItem]['start']);
+			isPlaying = false;
+		} else {
+			sendChange("start",feeder.feed[currentItem]['start']);
+			isPlaying = true;
+		}
+	};
+
+
+	/** PlayPause switch **/
+	private function setPlaypause() {
+		if(isPlaying == true) {
+			isPlaying = false;
+			sendChange("pause");
+		} else { 
+			isPlaying = true;
+			sendChange("start");
+		}
+	};
+
+
+	/** Play previous item. **/
+	private function setPrev() {
+		if(currentItem == 0) { 
+			setPlayitem(feeder.feed.length - 1); 
+		} else { 
+			setPlayitem(currentItem-1);
+		}
+	};
+
+
+	/** Play next item. **/
+	private function setNext() {
+		if(currentItem == feeder.feed.length - 1) { 
+			setPlayitem(0); 
+		} else { 
+			setPlayitem(currentItem+1);
+		}
+	};
+
+
+	/** Stop and clear item. **/
+	private function setStop() { 
+		sendChange("pause",0);
+		sendChange("stop");
+		sendChange("item",currentItem);
+		isPlaying = false;
+	};
+
+
+	/** Forward scrub number to model. **/
+	private function setScrub(prm) {
+		if(isPlaying == true) {
+			sendChange("start",prm);
+		} else {
+			sendChange("pause",prm);
+		}
+	};
+
+
+	/** Play a new item. **/
+	private function setPlayitem(itm:Number) {
+		if(itm != currentItem) {
+			itm > feeder.feed.length-1 ? itm = feeder.feed.length-1: null;
+			if(feeder.feed[currentItem]['file'] != feeder.feed[itm]['file']) {
+				sendChange("stop");
+			}
+			currentItem = itm;
+			sendChange("item",itm);
+
+		}
+		sendChange("start",feeder.feed[itm]["start"]);
+		currentURL = feeder.feed[itm]['file'];
+		isPlaying = true;
+	};
+
+
+	/** Get url from an item if link exists, else playpause. **/
+	private function setGetlink(idx:Number) {
+		if(feeder.feed[idx]["link"] == undefined) {
+			setPlaypause();
+		} else {
+			getURL(feeder.feed[idx]["link"],config["linktarget"]);
+		}
+	};
+
+
+	/** Determine what to do if an item is completed. **/
+	private function setComplete() {
+		itemsPlayed++;
+		if(feeder.feed[currentItem]['type'] == "rtmp" || 
+			config["streamscript"] != undefined) {
+			sendChange("stop");
+		}
+		if(config["repeat"] == "false" || (config["repeat"] == "list"
+			&& itemsPlayed >= feeder.feed.length)) {
+			sendChange("pause",0);
+			isPlaying = false;
+			itemsPlayed = 0;
+		} else {
+			var itm;
+			if(config["shuffle"] == "true") {
+				itm = randomizer.pick();
+			} else if(currentItem == feeder.feed.length - 1) {
+				itm = 0;
+			} else { 
+				itm = currentItem+1;
+			}
+			setPlayitem(itm);
+		}
+	};
+
+
+	/** Fullscreen switch function. **/
+	private function setFullscreen() {
+		if(Stage["displayState"] == "normal" && 
+			config["usefullscreen"] == "true") {
+			if(sizes[0] > 400) {
+				Stage["fullScreenSourceRect"] = new Rectangle(0,0,sizes[0],sizes[1]);
+			}
+			Stage["displayState"] = "fullScreen";
+		} else if (Stage["displayState"] == "fullScreen" && 
+			config["usefullscreen"] == "true") {
+			Stage["displayState"] = "normal";
+		} else if (config["fsbuttonlink"] != undefined) {
+			sendChange("stop");
+			getURL(config["fsbuttonlink"],config["linktarget"]);
+		}
+	};
+
+
+	/** Captions toggle **/
+	private function setCaptions() {
+		if(config["usecaptions"] == "true") {
+			config["usecaptions"] = "false";
+			config["clip"].captions._visible = false;
+			config["clip"].controlbar.cc.icn._alpha = 40;
+		} else {
+			config["usecaptions"] = "true";
+			config["clip"].captions._visible = true;
+			config["clip"].controlbar.cc.icn._alpha = 100;
+		}
+		playerSO.data.usecaptions = config["usecaptions"];
+		playerSO.flush();
+	};
+
+
+	/** Audiotrack toggle **/
+	private function setAudio() {
+		if(config["useaudio"] == "true") {
+			config["useaudio"] = "false";
+			config["clip"].audio.setStop();
+			config["clip"].controlbar.au.icn._alpha = 40;
+		} else {
+			config["useaudio"] = "true";
+			config["clip"].audio.setStart();
+			config["clip"].controlbar.au.icn._alpha = 100;
+		}
+		playerSO.data.useaudio = config["useaudio"];
+		playerSO.flush();
+	};
+
+
+	/** Check volume percentage and forward to models. **/
+	private function setVolume(prm) {
+		if (prm < 0 ) { prm = 0; } else if (prm > 100) { prm = 100; }
+		if(prm == 0) {
+			if(muted == true) {
+				muted = false;
+				sendChange("volume",config['volume']);
+			} else {
+				muted = true;
+				sendChange("volume",0);
+			}
+		} else {
+			sendChange("volume",prm);
+			config['volume'] = prm;
+			muted = false;
+		}
+		playerSO.data.volume = config["volume"];
+		playerSO.flush();
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/SearchView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/SearchView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/SearchView.as (revision 1)
@@ -0,0 +1,69 @@
+/**
+* Add a search bar to the player
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.SearchView extends AbstractView { 
+
+
+	/** Save the focus state (to capture enters) **/
+	public static var focussed:Boolean = false;
+
+	/** Constructor **/
+	function SearchView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		Key.addListener(this);
+		setDimensions();
+	};
+
+
+	/** Setup the dimensions of the search bar **/
+	private function setDimensions() {
+		var ref = this;
+		var tgt = config['clip'].search;
+		if(config["displayheight"] == config["height"] - config["searchbar"]) {
+			tgt._y = config['displayheight'];
+		} else {
+			tgt._y = config['displayheight'] + config['controlbar'];
+		}
+		tgt.ipt._width = config['width'] - 95;
+		tgt.ipt.onSetFocus = function() {
+			SearchView.focussed = true;
+		};
+		tgt.ipt.onKillFocus = function() {
+			SearchView.focussed = false;
+		};
+		tgt.bck._width = config['width'];
+		tgt.box._width = config['width'] - 95;
+		tgt.btn._x = config['width'] - 86;
+		tgt.btn.col = new Color(tgt.btn.icn);
+		tgt.btn.col.setRGB(config['frontcolor']);
+		tgt.btn.onRollOver = function() {
+			this.col.setRGB(ref.config['lightcolor']);
+		};
+		tgt.btn.onRollOut = function() {
+			this.col.setRGB(ref.config['frontcolor']);
+		};
+		tgt.btn.onRelease = function() {
+			ref.doSearch();
+		}
+	};
+
+
+	/** start the search function **/
+	private function doSearch() {
+		var txt = escape(config['clip'].search.ipt.text);
+		getURL(config['searchlink']+txt,config['linktarget']);
+	};
+
+
+	/** KeyDown handler, forwarded by Key object **/
+	public function onKeyDown() {
+		if(Key.getCode() == 13 && SearchView.focussed == true) { doSearch(); }
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/JavascriptView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/JavascriptView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/JavascriptView.as (revision 1)
@@ -0,0 +1,90 @@
+﻿/**
+* Javascript user interface management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.5
+**/
+
+
+import com.jeroenwijering.players.*;
+import flash.external.ExternalInterface;
+
+
+class com.jeroenwijering.players.JavascriptView extends AbstractView {
+
+
+	/** Previous loading value **/
+	private var loads:Number;
+	/** Previous elapsed value **/
+	private var elaps:Number;
+	/** Previous remaining value **/
+	private var remain:Number;
+	/** Previous state value **/
+	private var state:Number = 4;
+	/** Status change abbreviations **/
+	private var statuses:Array = new Array(
+		'PAUSED',
+		'BUFFERING',
+		'PLAYING',
+		'COMPLETE',
+		'NOT STARTED'
+	);
+
+
+	/** Constructor **/
+	function JavascriptView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		if(ExternalInterface.available) {
+			ExternalInterface.addCallback("sendEvent",this,sendEvent);
+		}
+	};
+
+
+	/** Override of the update receiver; forwarding all to javascript **/
+	public function getUpdate(typ:String,pr1:Number,pr2:Number) { 
+		if(ExternalInterface.available) {
+			switch(typ) {
+				case "load":
+					if(Math.round(pr1) != loads) {
+						loads = Math.round(pr1);
+						ExternalInterface.call("getUpdate",typ,loads,pr2,
+							config["javascriptid"]);
+					}
+					break;
+				case "time":
+					if(Math.round(pr1)!=elaps || Math.round(pr2)!=remain) {
+						elaps = Math.round(pr1);
+						remain = Math.round(pr2);
+						ExternalInterface.call("getUpdate",typ,elaps,remain,
+							config["javascriptid"]);
+					}
+					break;
+				case "item":
+					ExternalInterface.call("getUpdate",typ,pr1,pr2,
+						config["javascriptid"]);
+					break;
+				case "state":
+					sendStatusChange(pr1);
+					ExternalInterface.call("getUpdate",typ,pr1,pr2,
+						config["javascriptid"]);
+					break;
+				default:
+					ExternalInterface.call("getUpdate",typ,pr1,pr2,
+						config["javascriptid"]);
+					break;
+			}
+		}
+	};
+
+
+	/** state change function for longtail **/
+	private function sendStatusChange(stt) {
+		if(!(state == 3 && stt == 0)) {
+			ExternalInterface.call("playerStatusChange",
+				statuses[state],statuses[stt]);
+		}
+		state = stt;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/ImageModel.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/ImageModel.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/ImageModel.as (revision 1)
@@ -0,0 +1,151 @@
+﻿/**
+* Image model class of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.5
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.ImageLoader;
+
+
+class com.jeroenwijering.players.ImageModel extends AbstractModel { 
+
+
+	/** array with extensions used by this model **/
+	private var mediatypes:Array = new Array("jpg","gif","png","swf");
+	/** ImageLoader instance **/
+	private var imageLoader:ImageLoader;
+	/** Clip to load the image into **/
+	private var imageClip:MovieClip;
+	/** interval ID of image duration function **/
+	private var positionInterval:Number;
+	/** current state **/
+	private var currentState:Number;
+	/** boolean to check for current SWF **/
+	private var isSWF:Boolean;
+
+
+	/** Constructor **/
+	function ImageModel(vws:Array,ctr:AbstractController,cfg:Object,
+		fed:Object,imc:MovieClip,scl:Boolean) {
+		super(vws,ctr,cfg,fed);
+		imageClip = imc;
+		var ref = this;
+		if(arguments[5] == true) {
+			imageLoader = new ImageLoader(imageClip,config["overstretch"],
+				config["width"],config["height"]);
+		} else {
+			imageLoader = new ImageLoader(imageClip);
+		}
+		imageLoader.onLoadFinished = function() {
+			ref.currentState = 2;
+			ref.sendUpdate("state",2);
+			ref.sendUpdate("load",100);
+		};
+		imageLoader.onLoadProgress = function(tgt,btl,btt) {
+			ref.sendUpdate("load",Math.round(btl/btt*100));
+		};
+		imageLoader.onMetaData = function() {
+			ref.sendUpdate("size",this.sourceWidth,this.sourceHeight);
+			if(this.sourceLength > ref.feeder.feed[ref.currentItem]["duration"]) {
+				ref.feeder.feed[ref.currentItem]["duration"] = this.sourceLength;
+			}
+		};
+	};
+
+
+	/** Start display interval for a specific image **/
+	private function setStart(pos:Number) {
+		if(pos < 1 ) { 
+			pos = 0; 
+		} else if (pos > feeder.feed[currentItem]["duration"] - 1) { 
+			pos = feeder.feed[currentItem]["duration"] - 1; 
+		}
+		clearInterval(positionInterval);
+		if(feeder.feed[currentItem]["file"] != currentURL) {
+			imageClip._visible = true;
+			currentURL = feeder.feed[currentItem]["file"];
+			if(feeder.feed[currentItem]["file"].indexOf(".swf") == -1) {
+				isSWF = false;
+			} else {
+				isSWF = true;
+			}
+			imageLoader.loadImage(feeder.feed[currentItem]["file"]);
+			currentState = 1;
+			sendUpdate("state",1);
+			sendUpdate("load",0);
+		} else {
+			currentState = 2;
+			sendUpdate("state",2);
+		}
+		if (pos != undefined) { 
+			currentPosition = pos;
+			isSWF == true ? imageClip.mc.gotoAndPlay(pos*20): null;
+			if(pos == 0) {sendUpdate("time",0,feeder.feed[currentItem]["duration"]); }
+		} else { 
+			isSWF == true ? imageClip.mc.play(): null;
+		}
+		positionInterval = setInterval(this,"updatePosition",100);
+	};
+
+
+	/** Read and broadcast the current position of the song **/
+	private function updatePosition() {
+		if(currentState == 2) {
+			currentPosition += 0.1;
+			if(currentPosition >= feeder.feed[currentItem]["duration"]) {
+				currentState = 3;
+				sendUpdate("state",3);
+				sendCompleteEvent();
+			} else {
+				sendUpdate("time",currentPosition,feeder.feed[currentItem]["duration"]-currentPosition);
+			}
+		}
+	};
+
+
+	/** stop the image display interval **/
+	private function setPause(pos:Number) {
+		if(pos < 1 ) { 
+			pos = 0; 
+		} else if (pos > feeder.feed[currentItem]["duration"] - 1) { 
+			pos = feeder.feed[currentItem]["duration"] - 1; 
+		}
+		clearInterval(positionInterval);
+		currentState = 0;
+		sendUpdate("state",0);
+		if(pos != undefined) {
+			currentPosition = pos;
+			sendUpdate("time",currentPosition,feeder.feed[currentItem]["duration"]-currentPosition);
+			isSWF == true ? imageClip.mc.gotoAndStop(pos*20+1): null;
+		} else { 
+			isSWF == true ? imageClip.mc.stop(): null;
+		}
+	};
+
+
+	/** stop display of the item altogether **/
+	private function setStop() {
+		delete currentURL;
+		clearInterval(positionInterval);
+		currentPosition = 0;
+		isSWF == true ? imageClip.mc.gotoAndStop(1): null;
+		if (imageClip.bg == undefined) {
+			imageClip.mc.removeMovieClip();
+			imageClip.smc.removeMovieClip();
+			imageClip._visible = false; 
+		}
+	};
+
+
+	/** set duration to rotatetime for images **/
+	private function setItem(idx:Number) {
+		super.setItem(idx);
+		if(feeder.feed[currentItem]["duration"] == 0 && isActive == true) {
+			feeder.feed[currentItem]["duration"] = config['rotatetime'];
+		}
+	}
+
+}
Index: /trunk/as2/com/jeroenwijering/players/AbstractController.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/AbstractController.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/AbstractController.as (revision 1)
@@ -0,0 +1,207 @@
+﻿/**
+* Abstract controller class of the MCV pattern, extended by all controlllers.
+*
+* @author	Jeroen Wijering
+* @version	1.8
+**/
+
+
+import com.jeroenwijering.players.*;
+import com.jeroenwijering.utils.*;
+
+
+class com.jeroenwijering.players.AbstractController 
+	implements com.jeroenwijering.feeds.FeedListener {
+
+
+	/** Randomizer instance **/
+	private var randomizer:Randomizer;
+	/** array with all registered models **/
+	private var registeredModels:Array;
+	/** reference to the config array **/
+	private var config:Object;
+	/** reference to the feed array **/
+	private var feeder:Object;
+	/** Current item **/
+	private var currentItem:Number;
+	/** Current item **/
+	private var currentURL:String;
+	/** Current item **/
+	private var isPlaying:Boolean;
+	/** Number of items played: used for repeat=list **/
+	private var itemsPlayed:Number;
+	/** Array that saves the original video dimensions. **/
+	private var sizes:Array;
+
+
+	/** Constructor. **/
+	function AbstractController(cfg:Object,fed:Object) {
+		config = cfg;
+		feeder = fed;
+		feeder.addListener(this);
+	};
+
+
+	/** Complete the build of the MCV cycle and start flow of events. **/
+	public function startMCV(mar:Array) {};
+
+
+	/** Receive events from the views. **/
+	public function getEvent(typ:String,prm:Number,pr2:Number) {
+		//trace("controller: "+typ+": "+prm);
+		switch(typ) {
+			case "playpause": 
+				setPlaypause();
+				break;
+			case "prev":
+				if(noCommercial()) { setPrev(); }
+				break;
+			case "next":
+				if(noCommercial()) { setNext(); }
+				break;
+			case "stop":
+				if(noCommercial()) { setStop(); }
+				break;
+			case "scrub":
+				if(noCommercial()) { setScrub(prm); }
+				break;
+			case "volume":
+				setVolume(prm);
+				break;
+			case "playitem":
+				if(noCommercial()) { setPlayitem(prm); }
+				break;
+			case "getlink":
+				setGetlink(prm);
+				break;
+			case "fullscreen":
+				setFullscreen();
+				break;
+			case "complete":
+				setComplete();
+				break;
+			case "captions":
+				setCaptions();
+				break;
+			case "audio":
+				setAudio();
+				break;
+			case "size":
+				saveSizes(prm,pr2);
+				break;
+			default:
+				trace("controller: incompatible event received");
+				break;
+		}
+	};
+
+
+	/** Check for commercial **/
+	private function noCommercial():Boolean {
+		if(feeder.feed[currentItem]['category'] == 'commercial' ||
+			feeder.feed[currentItem]['category'] == 'preroll' || 
+			feeder.feed[currentItem]['category'] == 'postroll') {
+			return false;
+		} else {
+			return true;
+		}
+	};
+
+
+	/** PlayPause switch **/
+	private  function setPlaypause() {};
+
+
+	/** Play previous item. **/
+	private  function setPrev() {};
+
+
+	/** Play next item. **/
+	private function setNext() {};
+
+
+	/** Stop and clear item. **/
+	private function setStop() {};
+
+
+	/** Forward scrub number to model. **/
+	private function setScrub(prm:Number) {};
+
+
+	/** Play a new item. **/
+	private function setPlayitem(itm:Number) {
+		currentURL = feeder.feed[itm]['file'];
+	};
+
+
+	/** Get url from an item if link exists, else playpause. **/
+	private function setGetlink(idx:Number) {};
+
+
+	/** Determine what to do if an item is completed. **/
+	private function setComplete() {};
+
+
+	/** Volume event handler **/
+	private function setVolume(prm:Number) {};
+
+
+	/** Switch fullscreen mode **/
+	private function setFullscreen() {};
+
+
+	/** Switch captions on and off **/
+	private function setCaptions() {};
+
+
+	/** Switch captions on and off **/
+	private function saveSizes(pr1:Number,pr2:Number) {
+		sizes = new Array(pr1,pr2);
+	};
+
+
+	/** Switch audiotrack on and off **/
+	private function setAudio() {};
+
+
+	/** Sending changes to all registered models. **/
+	private function sendChange(typ:String,prm:Number):Void {
+		for(var i=0; i<registeredModels.length; i++) {
+			registeredModels[i].getChange(typ,prm);
+		}
+	};
+
+
+	/** check with feedupdates if current item is also changed **/
+	public function onFeedUpdate(typ:String) {
+		if(typ == 'new') {
+			setStop();
+			startMCV();
+		} else  if (typ == 'add') {
+			if (feeder.feed[currentItem+1]['file'] == currentURL) {
+				currentItem++;
+				sendChange("item",currentItem);
+			}
+			if(randomizer != undefined) {
+				randomizer = new Randomizer(feeder.feed);
+			}
+		} else if(typ == 'remove') {
+			if (feeder.feed[currentItem-1]['file'] == currentURL) {
+				currentItem--;
+				sendChange("item",currentItem);
+				if(randomizer != undefined) {
+					randomizer = new Randomizer(feeder.feed);
+				}
+			} else if(feeder.feed[currentItem]['file'] != currentURL) {
+				setStop();
+				startMCV();
+			} else {
+				if(randomizer != undefined) {
+					randomizer = new Randomizer(feeder.feed);
+				}
+			}
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/FLVModel.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/FLVModel.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/FLVModel.as (revision 1)
@@ -0,0 +1,335 @@
+﻿/**
+* FLV model class of the players MCV pattern.
+* Handles playback of FLV files, HTTP streams and RTMP streams.
+*
+* @author	Jeroen Wijering
+* @version	1.13
+**/
+
+
+import com.jeroenwijering.players.*;
+
+
+class com.jeroenwijering.players.FLVModel extends AbstractModel {
+
+
+	/** array with extensions used by this model **/
+	private var mediatypes:Array = new Array(
+		"flv","rtmp","mp4","m4v","m4a","mov","3gp","3g2");
+	/** NetConnection object reference **/
+	private var connectObject:NetConnection;
+	/** NetStream object reference **/
+	private var streamObject:NetStream;
+	/** Sound object reference **/
+	private var soundObject:Sound;
+	/** interval ID of the buffer update function **/
+	private var loadedInterval:Number;
+	/** current percentage of the video that's loaded **/
+	private var currentLoaded:Number = 0;
+	/** interval ID of the position update function **/
+	private var positionInterval:Number;
+	/** current state of the video that is playing **/
+	private var currentState:Number;
+	/** Current volume **/
+	private var currentVolume:Number;
+	/** MovieClip with "display" video Object  **/
+	private var videoClip:MovieClip;
+	/** object with keyframe times and positions, saved for PHP streaming **/
+	private var metaKeyframes:Object = new Object();
+	/** Boolean to check whether a stop event is fired **/
+	private var stopFired:Boolean = false;
+	/** Boolean to check whether a flusk event is fired **/
+	private var flushFired:Boolean = false;
+	/** Switch for FLV type currently played **/
+	private var flvType:String;
+	/** check h264 for time offset **/
+	private var isH264:Boolean;
+	/** check h264 for time offset **/
+	private var timeOffset:Number = 0;
+	/** reference to the captions object for parsing captionate data **/
+	public var capView:Object;
+	/** buffer iterator (prevents buffericon showing on slow PC's) **/
+	public var bufferCount:Number = 0;
+
+
+
+	/** Constructor **/
+	function FLVModel(vws:Array,ctr:AbstractController,cfg:Object,
+		fed:Object,fcl:MovieClip) {
+		super(vws,ctr,cfg,fed);
+		connectObject = new NetConnection();
+		videoClip = fcl;
+		if(config["smoothing"] == "false") {
+			videoClip.display.smoothing = false;
+			videoClip.display.deblocking = 1;
+		} else {
+			videoClip.display.smoothing = true;
+			videoClip.display.deblocking = 3;
+		}
+		videoClip.createEmptyMovieClip("snd",videoClip.getNextHighestDepth());
+		soundObject = new Sound(videoClip.snd);
+	};
+
+
+	/** Check which FLV type we use **/
+	private function setItem(idx:Number) {
+		super.setItem(idx);
+		if(isActive == true) {
+			if(config["streamscript"] != undefined) { 
+				flvType = "HTTP";
+			} else if(feeder.feed[currentItem]["type"] == "rtmp") {
+				flvType = "RTMP"; 
+			} else { 
+				flvType = "FLV"; 
+			}
+		}
+	};
+
+
+	/** Start a specific video **/
+	private function setStart(pos:Number) {
+		stopFired = false;
+		flushFired = false;
+		if (pos != undefined) { currentPosition = pos; }
+		if(pos < 1) {
+			pos = 0; 
+		} else if (pos > feeder.feed[currentItem]["duration"] - 1) { 
+			pos = feeder.feed[currentItem]["duration"] - 1; 
+		}
+		if (flvType=="RTMP" && feeder.feed[currentItem]["id"] != currentURL) {
+			connectObject.connect(feeder.feed[currentItem]["file"]);
+			currentURL = feeder.feed[currentItem]["id"];
+			setStreamObject(connectObject);
+			streamObject.play(currentURL);
+		} else if(flvType != "RTMP" && 
+			feeder.feed[currentItem]["file"] != currentURL) {
+			connectObject.connect(null);
+			currentURL = feeder.feed[currentItem]["file"];
+			if(flvType == "HTTP" ) {
+				setStreamObject(connectObject);
+				if(config["streamscript"] == "lighttpd") {
+					streamObject.play(currentURL);
+				} else {
+					streamObject.play(config["streamscript"] + 
+					"?file=" + currentURL);
+				}
+			} else {
+				setStreamObject(connectObject);
+				streamObject.play(currentURL);
+			}
+		} else {
+			if(flvType == "HTTP" && pos != undefined) {
+				playKeyframe(currentPosition);
+			} else if (flvType != "HTTP" && pos != undefined) {
+				streamObject.seek(currentPosition);
+				streamObject.pause(false);
+			} else if(flvType == "RTMP" && currentPosition > 0 && 
+				feeder.feed[currentItem]["duration"] == 0) {
+				connectObject.connect(feeder.feed[currentItem]["file"]);
+				setStreamObject(connectObject);
+				streamObject.play(currentURL);
+			} else {
+				streamObject.pause(false);
+			}
+		}
+		videoClip._visible = true;
+		videoClip._parent.thumb._visible = false;
+		clearInterval(positionInterval);
+		positionInterval = setInterval(this,"updatePosition",100);
+		clearInterval(loadedInterval);
+		loadedInterval = setInterval(this,"updateLoaded",100);
+	};
+
+
+	/** Read and broadcast the amount of the flv that's currently loaded **/
+	private function updateLoaded() {
+		var pct:Number = Math.round(streamObject.bufferLength/
+			streamObject.bufferTime*100);
+		if(flvType == "FLV") {
+			pct = Math.round(streamObject.bytesLoaded/
+				streamObject.bytesTotal*100);
+		}
+		if(isNaN(pct)) { 
+			currentLoaded = 0;
+			sendUpdate("load",0);
+		} else if (pct > 95) {
+			clearInterval(loadedInterval);
+			currentLoaded = 100;
+			sendUpdate("load",100);
+		} else if (pct != currentLoaded) { 
+			currentLoaded= pct;
+			sendUpdate("load",currentLoaded);
+		}
+	};
+
+
+	/** Read and broadcast the current position of the song **/
+	private function updatePosition() {
+		var pos = streamObject.time + timeOffset;
+		if(pos == currentPosition && currentState != 1 && stopFired != true) {
+			if(bufferCount == 5) { 
+				currentState = 1;
+				sendUpdate("state",1);
+				bufferCount = 0;
+			} else { 
+				bufferCount++;
+			}
+		} else if (pos != currentPosition && currentState != 2) { 
+			bufferCount = 0;
+			currentState = 2;
+			sendUpdate("state",2);
+		} else {
+			bufferCount = 0;
+		}
+		if (pos != currentPosition) {
+			currentPosition = pos;
+			sendUpdate("time",currentPosition,
+				Math.max(feeder.feed[currentItem]["duration"]-currentPosition,0));
+		} else if (stopFired == true || 
+			(flushFired == true && flvType != "RTMP" && bufferCount == 5)) {
+			currentState = 3;
+			videoClip._visible = false;
+			videoClip._parent.thumb._visible = true;
+			sendUpdate("state",3);
+			sendCompleteEvent();
+			stopFired = false;
+			flushFired = false;
+		}
+	};
+
+
+	/** Pause the video that's currently playing. **/
+	private function setPause(pos:Number) {
+		if(pos < 1) { pos = 0; }
+		clearInterval(positionInterval);
+		if(pos != undefined) {
+			currentPosition = pos;
+			sendUpdate("time",currentPosition,
+				Math.abs(feeder.feed[currentItem]["duration"]-currentPosition));
+			streamObject.seek(currentPosition);
+		}
+		streamObject.pause(true);
+		currentState = 0;
+		sendUpdate("state",0);
+	};
+
+
+	/** Stop video and clear data. **/
+	private function setStop(pos:Number) {
+		clearInterval(loadedInterval);
+		clearInterval(positionInterval);
+		videoClip._visible = false;
+		delete currentURL;
+		delete currentLoaded;
+		delete currentPosition;
+		delete metaKeyframes;
+		currentLoaded = 0;
+		stopFired = false;
+		timeOffset = 0;
+		streamObject.close();
+		delete streamObject;
+		videoClip.display.clear();
+	};
+
+
+	/** Set volume of the sound object. **/
+	private function setVolume(vol:Number) {
+		super.setVolume(vol);
+		currentVolume = vol;
+		soundObject.setVolume(vol);
+	};
+
+
+	/** Connect a new stream object to video/audio/callbacks **/
+	private function setStreamObject(cnt:NetConnection) {
+		_root.tf.text = 'metadata!';
+		var ref = this;
+		currentLoaded = 0;
+		sendUpdate("load",0);
+		streamObject = new NetStream(cnt);
+		streamObject.setBufferTime(config["bufferlength"]);
+		streamObject.onMetaData = function(obj) {
+			for (var i in obj) {
+				trace(i+': '+obj[i]);
+			}
+			if(obj.duration > 1) {
+				ref.feeder.feed[ref.currentItem]["duration"] = obj.duration;
+			}
+			if(obj.width > 10) {
+				ref.sendUpdate("size",obj.width,obj.height);
+			}
+			if(obj.seekpoints != undefined) {
+				ref.isH264 = true;
+				ref.metaKeyframes = new Object();
+				ref.metaKeyframes.times = new Array();
+				ref.metaKeyframes.filepositions = new Array();
+				for (var j in obj.seekpoints) {
+					ref.metaKeyframes.times.unshift(
+						Number(obj.seekpoints[j]['time']));
+					ref.metaKeyframes.filepositions.unshift(
+						Number(obj.seekpoints[j]['time']));
+				}
+			} else {
+				ref.metaKeyframes = obj.keyframes;
+			}
+			if(ref.feeder.feed[ref.currentItem]['start'] > 0) {
+				if(ref.flvType == "HTTP") {
+					ref.playKeyframe(ref.feeder.feed[ref.currentItem]['start']);
+				} else if (ref.flvType == "RTMP") {
+					ref.setStart(ref.feeder.feed[ref.currentItem]['start']);
+				}
+			}
+			delete obj;
+			delete this.onMetaData;
+		};
+		streamObject.onStatus = function(object) {
+			trace("status: "+object.code);
+			if(object.code == "NetStream.Play.Stop" && ref.flvType!='RTMP') {
+				ref.stopFired = true;
+			} else if (object.code == "NetStream.Play.StreamNotFound") {
+				ref.currentState = 3;
+				ref.videoClip._visible = false;
+				ref.sendUpdate("state",3);
+				ref.sendCompleteEvent();
+				ref.stopFired = false;
+				ref.flushFired = false;
+			} else if (object.code == "NetStream.Buffer.Flush") {
+				ref.flushFired = true;
+			}
+		};
+		streamObject.onPlayStatus = function(object) {
+			if( object.code == "NetStream.Play.Complete" ||
+				object.code == "NetStream.Play.Stop") {
+				ref.stopFired = true;
+			}
+		}; 
+		streamObject.onCaption = function(cap:Array) {
+			ref.capView.onCaptionate(cap);
+		};
+		videoClip.display.attachVideo(streamObject);
+		videoClip.snd.attachAudio(streamObject);
+	};
+
+
+	/** Play from keyframe position from metadata **/
+	private function playKeyframe(pos:Number) {
+		for (var i=0; i< metaKeyframes.times.length; i++) {
+			if((metaKeyframes.times[i] <= pos) && 
+				(metaKeyframes.times[i+1] >= pos)) {
+				if(config["streamscript"] == "lighttpd") {
+					streamObject.play(currentURL+"?start="+
+						metaKeyframes.filepositions[i]);
+					if(isH264 == true) {
+						timeOffset = metaKeyframes.filepositions[i];
+					}
+				} else {
+					streamObject.play(config["streamscript"]+"?file="+
+						currentURL+"&pos="+metaKeyframes.filepositions[i]);
+				}
+				break;
+			}
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/players/CaptionsView.as
===================================================================
--- /trunk/as2/com/jeroenwijering/players/CaptionsView.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/players/CaptionsView.as (revision 1)
@@ -0,0 +1,144 @@
+﻿/**
+* Captions display management of the players MCV pattern.
+*
+* @author	Jeroen Wijering
+* @version	1.4
+**/
+
+
+import com.jeroenwijering.players.*;
+import flash.filters.DropShadowFilter;
+
+
+class com.jeroenwijering.players.CaptionsView extends AbstractView { 
+
+
+	/** The current volume **/
+	private var parser:CaptionsParser;
+	/** The captions array **/
+	private var captions:Array;
+	/** The current elapsed time **/
+	private var currentTime:Number;
+	/** The captions textfield **/
+	private var clip:MovieClip;
+	/** Boolean for captionate captions **/
+	private var captionate:Boolean = false;
+	/** Time of last caption **/
+	private var capTime:Number;
+	/** Captionate track to use **/
+	private var capTrack:Number = 0;
+
+
+	/** Constructor, loads caption file. **/
+	function CaptionsView(ctr:AbstractController,cfg:Object,fed:Object) {
+		super(ctr,cfg,fed);
+		var ref = this;
+		Stage.addListener(this);
+		parser = new CaptionsParser();
+		parser.onParseComplete = function() {
+			this.parseArray.sortOn("bgn",Array.NUMERIC);
+			ref.captions = this.parseArray;
+			delete this;
+		}
+		clip = config["clip"].captions;
+		setDimensions();
+	};
+
+
+	/** onLoad override, sets capture sizes. **/
+	private function setDimensions() {
+		clip.txt.autoSize = "center";
+		clip.bck._height = clip.txt._height + 10;
+		if(Stage["displayState"] == "fullScreen") {
+			clip._width = Stage.width;
+			clip._yscale= clip._xscale;
+			clip._y = Stage.height - clip._height;
+		} else {
+			clip._width = config["displaywidth"];
+			clip._yscale = clip._xscale;
+			clip._y = config["displayheight"] - clip._height;
+		}
+		if(System.capabilities.version.indexOf("7,0,") == -1) {
+			var blr = 2 + Math.round(clip._yscale/100);
+			var flt = new flash.filters.DropShadowFilter(
+				0,0,0x000000,1,blr,blr,50,2);
+			clip.filters = new Array(flt);
+		}
+	};
+
+
+	/** parse a new captions file every time an item is set **/
+	private function setItem(idx:Number) {
+		captions = new Array();
+		if(feeder.feed[idx]["captions"] == undefined) {
+			clip.bck._alpha = 0;
+		} else if(feeder.feed[idx]["captions"].indexOf("captionate") > -1 ||
+			feeder.feed[idx]["captions"] == "true") {
+			captionate = true;
+			var tck = Number(feeder.feed[idx]["captions"].substr(-1));
+			if(isNaN(tck)) { 
+				capTrack = 0;
+			} else {
+				capTrack = tck;
+			}
+		} else {
+			parser.parse(feeder.feed[idx]["captions"]);
+		}
+	};
+
+
+	/** Check elapsed time, evaluate captions every second. **/
+	private function setTime(elp:Number,rem:Number) {
+		currentTime = elp;
+		if (captionate == false) {
+			setCaption();
+		}
+	};
+
+
+	/** Check if a new caption should be displayed **/
+	private function setCaption() {
+		var nxt:Number = captions.length;
+		for (var i=0; i<captions.length; i++) {
+			if(captions[i]["bgn"] > currentTime) {
+				nxt = i;
+				break;
+			}
+		}
+		if(captions[nxt-1]["bgn"] + captions[nxt-1]["dur"] > currentTime) {
+			clip.txt.htmlText = captions[nxt-1]["txt"];
+			if(System.capabilities.version.indexOf("7,0,") > -1) {
+				clip.bck._alpha = 50;
+				clip.bck._height = Math.round(clip.txt._height + 10);
+			} else {
+				clip.bck._height = Math.round(clip.txt._height + 15);
+			}
+			if(Stage["displayState"] == "fullScreen") {
+				clip._y = Stage.height - clip._height;
+			} else {
+				clip._y = config["displayheight"] - clip._height;
+			}
+		} else {
+			clip.txt.htmlText = "";
+		}
+	};
+
+
+	/** Captionate input **/
+	public function onCaptionate(cap:Array) {
+		clip.txt.htmlText = cap[capTrack];
+		capTime = currentTime;
+	};
+
+
+	/** OnResize Handler: catches stage resizing **/
+	public function onResize() { setDimensions(); };
+
+
+	/** Catches fullscreen escape  **/
+	public function onFullScreen(fs:Boolean) { 
+		if(fs == false) { setDimensions(); }
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/XMLParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/XMLParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/XMLParser.as (revision 1)
@@ -0,0 +1,84 @@
+/**
+* Parse XML file and return a simple, associative array.
+*
+* @author	Jeroen Wijering
+* @version	1.2
+**/
+
+
+class com.jeroenwijering.utils.XMLParser {
+
+
+	/** Flash XML object the file is loaded into. **/
+	private var input:XML;
+	/** The object the XML is parsed into **/
+	private var output:Object;
+
+
+	/** Constructor, sets up XML object **/
+	function XMLParser() {};
+
+
+	/** Start parsing **/
+	public function parse(lnk:String) {
+		var ref = this;
+		input = new XML();
+		output = new Object();
+		input.ignoreWhite = true;
+		input.onLoad = function(scs:Boolean) {
+			if(scs) {
+				ref.processRoot();
+			} else {
+				ref.onError();
+			}
+		};
+		if(_root._url.indexOf("file://") > -1) {
+			input.load(lnk); 
+		} else if(lnk.indexOf('?') > -1) {
+			input.load(lnk+'&'+random(999));
+		} else { 
+			input.load(lnk+'?'+random(999));
+		}
+	};
+
+
+	/** Process the root XML node **/
+	private function processRoot() {
+		processNode(input.firstChild,output);
+		delete input;
+		onComplete(output);
+	};
+
+
+	/** Process a specific node **/
+	private function processNode(nod:XMLNode,obj:Object) {
+		obj['name'] = nod.nodeName;
+		for(var att in nod.attributes) {
+			obj[att] = nod.attributes[att];
+		}
+		if(nod.childNodes.length < 2 && nod.firstChild.nodeName == null) {
+			obj['value'] = nod.firstChild.nodeValue;
+		} else {
+			obj['childs'] = new Array();
+			var chn = nod.firstChild;
+			var i = 0;
+			while(chn != undefined) {
+				var cob = new Object();
+				processNode(chn,cob);
+				obj['childs'].push(cob);
+				chn = chn.nextSibling;
+				i++;
+			}
+		}
+	};
+
+
+	/** Invoked when parsing is completed. **/
+	public function onComplete(obj:Object) {};
+
+
+	/** Invoked when parsing is completed. **/
+	public function onError() {};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/ConfigManager.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/ConfigManager.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/ConfigManager.as (revision 1)
@@ -0,0 +1,124 @@
+﻿/**
+* Config variables loading and management. Also sets stage and rightclickmenu.
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.utils.XMLParser;
+
+
+class com.jeroenwijering.utils.ConfigManager {
+
+
+	/** Array with configuration values **/
+	private var config:Object;
+	/** XML parsing object **/
+	private var parser:XMLParser;
+	/** cookie parsing object **/
+	private var cookie:SharedObject;
+	/** do stage setup as well **/
+	private var staging:Boolean;
+	/** reference to the contextmenu **/
+	public var context:ContextMenu;
+
+
+	/** 
+	* Constructor.
+	*
+	* @param stg	Switch for doing stage setup.
+	**/
+	function ConfigManager(stg:Boolean) {
+		staging = stg;
+		if(staging == true) {
+			Stage.scaleMode = "noScale";
+			Stage.align = "TL";
+		}
+	};
+
+
+	/** 
+	* Load configuration array.
+	*
+	* @param def	The object with default values.
+	**/
+	public function loadConfig(def:Object) {
+		config = def;
+		config["clip"]._visible = false;
+		if(staging == true && Stage.width > 1) {
+			config['width']  = Stage.width;
+			config['height'] = Stage.height;
+			config["clip"]._parent.activity._x = Stage.width/2;
+			config["clip"]._parent.activity._y = Stage.height/2;
+			config["clip"]._parent.activity._alpha = 100;
+		}
+		_root['config'] == undefined ? loadCookies(): loadFile();
+	};
+
+
+	/** Load configuration data from external XML file **/
+	private function loadFile() {
+		var ref = this;
+		parser = new XMLParser();
+		parser.onComplete = function(obj) {
+			var ret = new Object();
+			for(var i=0; i<obj.childs.length; i++) {
+				ret[obj.childs[i]['name']] = obj.childs[i]['value'];
+			}
+			ref.checkWrite(ret);
+			ref.loadCookies();
+		}
+		parser.parse(_root['config']);
+	};
+
+
+	/** load configuration data from flashcookies **/
+	private function loadCookies() {
+		cookie = SharedObject.getLocal("com.jeroenwijering.players", "/");
+		checkWrite(cookie.data);
+		loadVars();
+	};
+
+
+	/** Load configuration data from flashvars **/
+	private function loadVars() {
+		checkWrite(_root);
+		if(staging == true) {setContext(); }
+		onComplete();
+	};
+
+
+	/** Check if setting exists in defaults and overwrite. **/
+	private function checkWrite(dat:Object) {
+		for(var cfv in config) {
+			if(dat[cfv] != undefined && dat[cfv].indexOf('asfunction') == -1) {
+				config[cfv] = unescape(dat[cfv]);
+			}
+		}
+	};
+
+
+	/** Setup context menu. **/
+	private function setContext() {
+		var ref = this;
+		_root.ref = this;
+		context = new ContextMenu();
+		context.hideBuiltInItems();
+		var itm = new ContextMenuItem("About "+config['abouttxt']+"...",ref.goTo);
+		context.customItems.push(itm);
+		config["clip"]._parent.menu = context;
+	};
+
+
+	/** Context menu link jump. **/
+	public function goTo(obj,itm) {
+		getURL(obj.ref.config['aboutlnk'],'_blank');
+	};
+
+
+	/** Event handler for succesfull completion of all parsing **/
+	public function onComplete() {};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/Animations.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/Animations.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/Animations.as (revision 1)
@@ -0,0 +1,134 @@
+﻿/**
+* A couple of commonly used animation functions.
+*
+* @author	Jeroen Wijering
+* @version	1.12
+**/
+
+
+class com.jeroenwijering.utils.Animations {
+
+
+	/**
+	* Fadein function for MovieClip.
+	*
+	* @param tgt	The Movieclip to fade in.
+	* @param end	The final alpha value.
+	* @param spd	The amount of alpha change per frame.
+	**/
+	public static function fadeIn(tgt:MovieClip,end:Number,spd:Number) {
+		arguments.length < 3 ? spd = 20: null;
+		arguments.length < 2 ? end = 100: null;
+		tgt._visible = true;
+		tgt.onEnterFrame = function() {
+			if(this._alpha > end-spd) {
+				delete this.onEnterFrame;
+				this._alpha = end;
+			} else {
+				this._alpha += spd;
+			}
+		};
+	};
+
+
+	/**
+	* Fadeout function for MovieClip.
+	*
+	* @param tgt	The Movieclip to fade out.
+	* @param end	The final alpha value.
+	* @param spd	The amount of alpha change per frame.
+	* @param rmv	Removing the clip off stage switch.
+	**/
+	public static function fadeOut(tgt:MovieClip,end:Number,
+		spd:Number,rmv:Boolean) {
+		arguments.length < 4 ? rmv = false: null;
+		arguments.length < 3 ? spd = 20: null;
+		arguments.length < 2 ? end = 0: null;
+		tgt.onEnterFrame = function() {
+			if(this._alpha < end+spd) {
+				delete this.onEnterFrame;
+				this._alpha = end;
+				end == 0 ? this._visible = false: null;
+				rmv == true ? this.removeMovieClip(): null;
+			} else {
+				this._alpha -= spd;
+			}
+		};
+	};
+
+
+	/**
+	* Crossfade a given MovieClip through 0. 
+	*
+	* @param tgt	The Movieclip to crossfade.
+	* @param alp	The final alpha value.
+	**/
+	public static function crossfade(tgt:MovieClip, alp:Number) {
+		var phs = "out";
+		var pct = alp/5;
+		tgt.onEnterFrame = function() {
+			if(phs == "out") {
+				this._alpha -= pct;
+				if (this._alpha < 1) { phs = "in"; }
+			} else {
+				this._alpha += pct;
+				this._alpha >= alp ? delete this.onEnterFrame : null; 
+			}
+		}; 
+	};
+
+
+	/**
+	* Smoothly move a Movielip to a certain position.
+	*
+	* @param tgt	The Movielip to move.
+	* @param xps	The x destination.
+	* @param yps	The y destination.
+	* @param spd	The movement speed (1 - 2).
+	**/
+	public static function easeTo(tgt:MovieClip,xps:Number,yps:Number,
+		spd:Number) {
+		arguments.length < 4 ? spd = 2: null;
+		tgt.onEnterFrame = function() {
+			this._x = xps-(xps-this._x)/(1+1/spd);
+			this._y = yps-(yps-this._y)/(1+1/spd);
+			if (this._x>xps-1 && this._x<xps+1 && 
+				this._y>yps-1 && this._y<yps+1) {
+				this._x = Math.round(xps);
+				this._y = Math.round(yps);
+				delete this.onEnterFrame;
+			} 
+		}; 
+	};
+
+
+	/**
+	* Typewrite text into a textfield. 
+	*
+	* @param tgt	Movieclip that has a 'tf' TextField.
+	* @param txt	The textstring to write; uses current content if omitted.
+	* @param spd	The speed of typing (1 - 2).
+	**/
+	public static function easeText(tgt:MovieClip,txt:String,spd:Number) {
+		if (arguments.length < 2) {
+			tgt.str = tgt.tf.text;
+			tgt.hstr = tgt.tf.htmlText;
+		} else { 
+			tgt.str = tgt.hstr = txt; 
+		}
+		if (arguments.length < 3) { spd = 1.5; }
+		tgt.tf.text = "";
+		tgt.i = 0;
+		tgt.onEnterFrame = function() {
+			this.tf.text = this.str.substr(0, this.str.length - 
+				Math.floor((this.str.length - this.tf.text.length)/spd));
+			if(this.tf.text == this.str) {
+				this.tf.htmlText = this.hstr;
+				delete this.onEnterFrame;
+			}
+			this.i++;
+		};
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/BandwidthCheck.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/BandwidthCheck.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/BandwidthCheck.as (revision 1)
@@ -0,0 +1,82 @@
+/**
+* Check user bandwidth/connection speed over HTTP or RTMP.
+* 
+* @example 
+* var bwc = new BandwidthCheck("http://www.server.com/upload/100k.jpg");
+* bwc.onComplete = function(kbps) { trace(kbps); };
+*
+* @author	Brian Weil
+* @author	Stefan Richter
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+class com.jeroenwijering.utils.BandwidthCheck {
+
+
+	/** MovieClipLoader instance **/
+	private var loader:MovieClipLoader;
+	/** NetConnection instance **/
+	private var connector:NetConnection;
+	/** MovieClip  instance **/
+	private var clip:MovieClip;
+	/** Start time of test **/
+	private var startTime:Number;
+
+
+	/** Constructor for the BandwidthCheck **/
+	function BandwidthCheck(fil:String) {
+		var ref = this;
+		if (fil.indexOf("rtmp") == -1) {
+			loader = new MovieClipLoader;
+			loader.addListener(this);
+			clip = _root.createEmptyMovieClip("_bwchecker",1);
+			loader.loadClip(fil + "?" + random(9999),clip);
+		} else {
+			connector = new NetConnection();
+			connector.onStatus = function(info) {
+				if(info.code != "NetConnection.Connect.Success") {
+					ref.onComplete(0);
+				}
+			};
+			connector.connect(fil, true);
+			connector.onBWDone = function(kbps,dtd,dtt,lat) {
+				ref.onComplete(kbps);
+			};
+			connector.onBWCheck = function() {};
+		}
+	};
+
+
+	/** event handler for finished loading **/
+	private function onLoadComplete(tgt:MovieClip,hts:Number) {
+		tgt._visible = false;
+		var dat = new Date();
+		var ttl = clip.getBytesTotal();
+		var sec = (dat.getTime() - startTime)/1000;
+		var klb = ttl * 0.0078125*0.93;
+		var kbps = Math.floor(klb/sec);
+		onComplete(kbps);
+		clip.removeMovieClip();
+	};
+
+
+	/** event handler for loading error **/
+	private function onLoadError(tgt:MovieClip,err:String,stt:Number) {
+		onComplete(0);
+	};
+
+
+	/** event handler for loading start **/
+	private function onLoadStart() {
+		var d = new Date();
+		startTime = d.getTime();
+	};
+
+
+	/** event handler for completed test **/
+	public function onComplete() {};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/Scroller.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/Scroller.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/Scroller.as (revision 1)
@@ -0,0 +1,188 @@
+﻿/**
+* Manages scrolling of a designated MovieClip, automatic or with scrollbar.
+*
+* @example
+* var myScroller = new com.jeroenwijering.utils.Scroller(myMovie,myMask);
+* myscroller.scrollTo(200);
+*
+* @author	Jeroen Wijering
+* @version	1.8
+**/
+
+
+import com.jeroenwijering.utils.Animations;
+
+
+class com.jeroenwijering.utils.Scroller {
+
+
+	/** Movieclip that should be scrolled **/
+	private var targetClip:MovieClip;
+	/** Mask of the movieclip **/
+	private var maskClip:MovieClip;
+	/** Use automatic scroling, defaults to false **/
+	private var autoScroll:Boolean = false;
+	/** scrollbar front color **/
+	private var frontColor:Number = 0x000000;
+	/** scrollbar highlighting color **/
+	private var lightColor:Number = 0x000000;
+	/** size ratio clip:mask **/
+	private var sizeRatio:Number;
+	/** scroll interval id for autoscroller and dragging of scrollbar **/
+	private var scrollInterval:Number;
+	/** corrent scroll index **/
+	private var currentScroll:Number = 0;
+	/** autoscroll multiplier **/
+	private var AUTOSCROLL_SPEED:Number = 0.25;
+	/** Movieclip the scrollbar is drawn into **/
+	private var SCROLLER_CLIP:MovieClip;
+	/** Color object of the scrollbar back **/
+	private var SCROLLER_BACK_COLOR:Color;
+	/** Color object of the scrollbar front **/
+	private var SCROLLER_FRONT_COLOR:Color;
+
+
+	/** Sets up scrolling behaviour and scrollbar **/
+	function Scroller(tgt:MovieClip,msk:MovieClip,asc:Boolean,
+		fcl:Number,hcl:Number) {
+		targetClip = tgt;
+		maskClip = msk;
+		arguments.length > 2 ? autoScroll = asc: null;
+		arguments.length > 3 ? frontColor = fcl: null;
+		arguments.length > 4 ? lightColor = hcl: null;
+		sizeRatio = maskClip._height/targetClip._height;
+		if(autoScroll == false) {
+			drawScrollbar();
+		} else {
+			scrollInterval = setInterval(this,"doAutoscroll",50);
+		}
+		if(System.capabilities.os.toLowerCase().indexOf("mac") == -1) {
+			Mouse.addListener(this);
+		}
+	};
+
+
+	/** Draw the scrollbar. **/
+	private function drawScrollbar() {
+		targetClip._parent.createEmptyMovieClip("scrollbar",
+			targetClip._parent.getNextHighestDepth());
+		SCROLLER_CLIP = targetClip._parent.scrollbar;
+		SCROLLER_CLIP._x = maskClip._x+maskClip._width - 1;
+		SCROLLER_CLIP._y = maskClip._y+3;
+		SCROLLER_CLIP.createEmptyMovieClip("back",0);
+		SCROLLER_CLIP.back._alpha = 0;
+		SCROLLER_CLIP.back._y = -3;
+		drawSquare(SCROLLER_CLIP.back,12,maskClip._height,frontColor);
+		SCROLLER_CLIP.createEmptyMovieClip("bar",1);
+		SCROLLER_CLIP.bar._x = 4;
+		SCROLLER_CLIP.bar._alpha = 50;
+		drawSquare(SCROLLER_CLIP.bar,4,maskClip._height-5,frontColor);
+		SCROLLER_CLIP.createEmptyMovieClip("front",2);
+		SCROLLER_CLIP.front._x = 3;
+		drawSquare(SCROLLER_CLIP.front,6,
+			SCROLLER_CLIP.bar._height*sizeRatio,frontColor);
+		SCROLLER_CLIP.front.createEmptyMovieClip("bg",1);
+		SCROLLER_CLIP.front.bg._x = -3;
+		SCROLLER_CLIP.front.bg._alpha = 0;
+		drawSquare(SCROLLER_CLIP.front.bg,12,
+			SCROLLER_CLIP.front._height,frontColor);
+		SCROLLER_FRONT_COLOR = new Color(SCROLLER_CLIP.front);
+		setScrollbarEvents();
+	};
+
+
+	/** Set use of mousewheel to scroll playlist. **/
+	public function onMouseWheel(dta:Number) { 
+		scrollTo(currentScroll-dta*20); 
+	};
+
+
+	/** Set autoscroll events. **/
+	private function doAutoscroll() {
+		if (maskClip._xmouse>0 && maskClip._xmouse<maskClip._width/
+			(maskClip._xscale/100) && maskClip._ymouse>0 && 
+			maskClip._ymouse<maskClip._height/(maskClip._yscale/100)) {
+			var dif:Number = 
+				maskClip._ymouse*(maskClip._yscale/100)-maskClip._height/2;
+			scrollTo(currentScroll+Math.floor(dif*AUTOSCROLL_SPEED));
+		}
+	};
+
+
+	/** All scrollbar mouse events grouped together. **/
+	private function setScrollbarEvents():Void {
+		var instance:Scroller = this;
+		SCROLLER_CLIP.front.onRollOver = 
+			SCROLLER_CLIP.back.onRollOver = function() {
+			instance.SCROLLER_FRONT_COLOR.setRGB(instance.lightColor);
+		};
+		SCROLLER_CLIP.front.onRollOut = 
+			SCROLLER_CLIP.back.onRollOut = function() {
+			instance.SCROLLER_FRONT_COLOR.setRGB(instance.frontColor);
+		};
+		SCROLLER_CLIP.back.onRelease = function() { 
+			if(this._ymouse > this._parent.front._y + 
+				this._parent.front._height) { 
+				instance.scrollTo(instance.currentScroll + 
+					instance.maskClip._height/2); 
+			} else if (this._ymouse < this._parent.front._y) { 
+				instance.scrollTo(instance.currentScroll - 
+					instance.maskClip._height/2); 
+			}
+		};
+		SCROLLER_CLIP.front.onPress = function() { 
+			this.startDrag(false,3,0,3,instance.SCROLLER_CLIP.bar._height - 
+				this._height);
+			instance.scrollInterval = setInterval(instance,"scrollTo",100);
+		};
+		SCROLLER_CLIP.front.onRelease = 
+			SCROLLER_CLIP.front.onReleaseOutside = function() { 
+			this.stopDrag();
+			clearInterval(instance.scrollInterval);
+		};
+		scrollTo(maskClip._y - targetClip._y);
+	};
+
+
+	/** Scroll the MovieClip to a given Y position. **/
+	public function scrollTo(yps:Number):Void {
+		if(arguments.length == 0 && autoScroll == false) {
+			yps = SCROLLER_CLIP.front._y*maskClip._height / 
+				SCROLLER_CLIP.front._height;
+		}
+		if(yps<5) {
+			yps=0;
+		} else if (yps>targetClip._height-maskClip._height-5) {
+			yps = targetClip._height - maskClip._height;
+		}
+		Animations.easeTo(targetClip,targetClip._x,maskClip._y - yps);
+		SCROLLER_CLIP.front._y = yps*SCROLLER_CLIP.front._height / 
+			maskClip._height;
+		currentScroll = yps;
+	};
+
+
+	/** Remove the scrollbar from stage **/
+	public function purgeScrollbar() {
+		clearInterval(scrollInterval);
+		Mouse.removeListener(this);
+		scrollTo(0);
+		SCROLLER_CLIP.removeMovieClip();
+	};
+
+
+	/** Draw a square in a given movieclip. **/
+	private function drawSquare(tgt:MovieClip,wth:Number,hei:Number,
+		clr:Number) {
+		tgt.clear();
+		tgt.beginFill(clr,100);
+		tgt.moveTo(0,0);
+		tgt.lineTo(wth,0);
+		tgt.lineTo(wth,hei);
+		tgt.lineTo(0,hei);
+		tgt.lineTo(0,0);
+		tgt.endFill();
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/StringMagic.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/StringMagic.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/StringMagic.as (revision 1)
@@ -0,0 +1,92 @@
+﻿/**
+* A couple of commonly used string operations.
+*
+* @author	Jeroen Wijering
+* @version	1.3
+**/
+
+
+class com.jeroenwijering.utils.StringMagic {
+
+
+	/** Strip tags and breaks from a string. **/
+	static function stripTagsBreaks(str:String):String {
+		if(str.length == 0 || str == undefined) { return ""; }
+		var tmp:Array = str.split("\n");
+		str = tmp.join("");
+		tmp = str.split("\r");
+		str = tmp.join("");
+		var i:Number = str.indexOf("<");
+		while(i != -1) {
+			var j = str.indexOf(">",i+1);
+			j == -1 ? j = str.length-1: null;
+			str = str.substr(0,i) + str.substr(j+1,str.length);
+			i = str.indexOf("<",i);
+		}
+		return str;
+	};
+
+
+	/** 
+	* Chop string into a number of lines. 
+	* 
+	* @param str	The string to chop.
+	* @param cap	The maximum number of characters per line.
+	* @param nbr	The maximum number of lines.
+	**/
+	static function chopString(str:String,cap:Number,nbr:Number):String {
+		for(var i=cap; i<str.length; i+=cap) {
+			if(i == cap*nbr) {
+				if(str.indexOf(" ",i-5) == -1) {
+					return str;
+				} else {
+					return str.substr(0,str.indexOf(" ",i-5));
+				}
+			} else  if(str.indexOf(" ",i) > 0) {
+				str = str.substr(0,str.indexOf(" ",i-3)) + "\n" +
+					str.substr(str.indexOf(" ",i-3)+1);
+			}
+		}
+		return str;
+	};
+
+
+	/** Add a leading zero and convert number to string. **/
+	static function addLeading(nbr:Number):String { 
+		if(nbr < 10) { 
+			return "0"+Math.floor(nbr); 
+		} else { 
+			return Math.floor(nbr).toString(); 
+		}
+	};
+
+
+	/** 
+	* Convert a string to seconds, with these formats supported:
+	* 00:03:00.1 / 03:00.1 / 180.1s / 3.2m / 3.2h
+	**/
+	static function toSeconds(str:String):Number {
+		var arr = str.split(':');
+		var sec;
+		if (str.substr(-1) == 's') {
+			sec = Number(str.substr(0,str.length-2));
+		} else if (str.substr(-1) == 'm') {
+			sec = Number(str.substr(0,str.length-2))*60;
+		} else if(str.substr(-1) == 'h') {
+			sec = Number(str.substr(0,str.length-2))*3600;
+		} else if(arr.length > 1) {
+			sec = Number(arr[arr.length-1]);
+			sec += Number(arr[arr.length-2])*60;
+			sec += Number(arr[arr.length-3])*3600;
+		} else {
+			sec = Number(str);
+		}
+		if(isNaN(sec)) {
+			return 0;
+		} else {
+			return sec;
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/ImageLoader.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/ImageLoader.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/ImageLoader.as (revision 1)
@@ -0,0 +1,159 @@
+﻿/**
+* Class for loading, scaling and smoothing images to a given MovieClip.
+*
+* @example
+* var myLoader = new ImageLoader(this,"true",400,300);
+* myLoader.loadImage("somephoto.jpg");
+* 
+* @author	Jeroen Wijering
+* @version	1.11
+**/
+
+
+class com.jeroenwijering.utils.ImageLoader {
+
+
+	/** MovieClip Loader Instance **/
+	private var mcLoader:MovieClipLoader;
+	/** Target MovieClip **/
+	private var targetClip:MovieClip;
+	/** Target Width **/
+	private var targetWidth:Number;
+	/** Target Height **/
+	private var targetHeight:Number;
+	/** Source URL **/
+	private var sourceURL:String;
+	/** Source Width **/
+	private var sourceWidth:Number;
+	/** Source Height **/
+	private var sourceHeight:Number;
+	/** Source Length (for SWF) **/
+	private var sourceLength:Number;
+	/** Overstretch Boolean **/
+	private var overStretch:String = "none";
+	/** Boolean that checks whether an SWF is loaded **/
+	private var useSmoothing:Boolean;
+	/** Interval for SWF meta checking **/
+	private var metaInt:Number;
+
+
+	/** Constructor for the ImageLoader **/
+	function ImageLoader(tgt:MovieClip,ost:String,wid:Number,hei:Number) {
+		targetClip = tgt;
+		arguments.length > 1 ? overStretch = String(ost): null;
+		if(arguments.length > 2) { 
+			targetWidth = wid;
+			targetHeight = hei;
+		}
+		mcLoader = new MovieClipLoader();
+		mcLoader.addListener(this);
+	};
+
+
+	/** Switch image with bitmaparray if possible. **/
+	public function onLoadInit(inTarget:MovieClip):Void {
+		if(useSmoothing  == 'true') {
+			var bmp = new flash.display.BitmapData(targetClip.mc._width,
+				targetClip.mc._height, true, 0x000000);
+			bmp.draw(targetClip.mc);
+			var bmc:MovieClip = targetClip.createEmptyMovieClip("smc",
+				targetClip.getNextHighestDepth());
+			bmc.attachBitmap(bmp, bmc.getNextHighestDepth(),"auto",true);
+			targetClip.mc.unloadMovie();
+			targetClip.mc.removeMovieClip();
+			delete targetClip.mc;
+			scaleImage(targetClip.smc);
+			onLoadFinished();
+		} else {
+			targetClip.mc.forceSmoothing = true;
+			if(sourceURL.toLowerCase().indexOf(".swf") == -1) {
+				scaleImage(targetClip.mc);
+			}
+			onLoadFinished();
+		}
+	};
+
+
+	/** Scale the image while maintaining aspectratio **/
+	private function scaleImage(tgt:MovieClip):Void {
+		targetClip._xscale = targetClip._yscale = 100;
+		var tcf = tgt._currentframe;
+		tgt.gotoAndStop(1);
+		sourceWidth = tgt._width;
+		sourceHeight = tgt._height;
+		sourceLength = tgt._totalframes/20;
+		var xsr = targetWidth/sourceWidth;
+		var ysr = targetHeight/sourceHeight;
+		if (overStretch == "fit" || Math.abs(xsr-ysr) < 0.1) {
+			tgt._width = targetWidth;
+			tgt._height = targetHeight;
+		} else if ((overStretch == "true" && xsr > ysr) || 
+			(overStretch == "false" && xsr < ysr)) { 
+			tgt._xscale = tgt._yscale = xsr*100;
+		} else if(overStretch == "none") {
+			tgt._xscale = tgt._yscale = 100;
+		} else { 
+			tgt._xscale = tgt._yscale = ysr*100;
+		}
+		if(targetWidth != undefined) {
+			tgt._x = targetWidth/2 - tgt._width/2;
+			tgt._y = targetHeight/2 - tgt._height/2;
+		}
+		tgt.gotoAndPlay(tcf);
+		onMetaData();
+	};
+
+
+	/** Start loading an image. **/
+	public function loadImage(img:String):Void {
+		sourceURL = img;
+		targetClip.mc.clear();
+		targetClip.smc.unloadMovie();
+		targetClip.smc.removeMovieClip();
+		delete targetClip.smc;
+		checkSmoothing(img);
+		var raw:MovieClip = targetClip.createEmptyMovieClip("mc",1);
+		mcLoader.loadClip(img,raw);
+		if(img.toLowerCase().indexOf(".swf") > -1) {
+			metaInt = setInterval(this,"setSWFMeta",200);
+		}
+	};
+
+
+	/** Check whether smoothing can be enabled. **/
+	private function checkSmoothing(img:String):Void {
+		var idx:Number = _root._url.indexOf("/",8);
+		var rot:String = _root._url.substring(0,idx);
+		if(System.capabilities.version.indexOf("7,0,") > -1 ||
+			img.toLowerCase().indexOf(".swf") > -1 || 
+			_root._url.indexOf("file://") > -1 || 
+			(img.indexOf(rot) == -1 && img.indexOf('http://') == 0)) {
+			useSmoothing = false;
+		} else {
+			useSmoothing = true;
+		}
+	};
+
+
+	/** Check when to set the SWF metadata **/
+	private function setSWFMeta() {
+		if(targetClip.mc._currentframe > 0) {
+			clearInterval(metaInt);
+			scaleImage(targetClip.mc);
+		}
+	};
+
+
+	/** Event handler; invoked when loading. **/
+	public function onLoadProgress(tgt:MovieClip,btl:Number,btt:Number) {};
+
+
+	/** Event handler; invoked when image is completely loaded. **/
+	public function onLoadFinished() {};
+
+
+	/** Event handler; invoked when metadata is received. **/
+	public function onMetaData() {};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/utils/Randomizer.as
===================================================================
--- /trunk/as2/com/jeroenwijering/utils/Randomizer.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/utils/Randomizer.as (revision 1)
@@ -0,0 +1,43 @@
+﻿/**
+* Pick random array indexes without having the same picked twice times.
+* 
+* @author	Jeroen Wijering
+* @version	1.2
+**/
+
+
+class com.jeroenwijering.utils.Randomizer {
+
+
+	/** a reference of the original array **/
+	private var originalArray:Array;
+	/** a copy of the original array **/
+	private var bufferArray:Array;
+
+
+	/** 
+	* Constructor.
+	*
+	* @param arr	Array to randomize.
+	**/
+	public function Randomizer(arr:Array) {
+		originalArray = arr;
+		bufferArray = new Array();
+	};
+
+
+	/** Randomly pick an index from the array given. **/
+	public function pick():Number {
+		if(bufferArray.length == 0) {
+			for(var k=0; k<originalArray.length; k++) {
+				bufferArray.push(k);
+			}
+		}
+		var ran = random(bufferArray.length);
+		var idx = bufferArray[ran];
+		bufferArray.splice(ran,1);
+		return idx;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/ATOMParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/ATOMParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/ATOMParser.as (revision 1)
@@ -0,0 +1,91 @@
+﻿/**
+* Parses ATOM feeds and returns an indexed array with all elements
+*
+* @author	Jeroen Wijering
+* @version	1.4
+**/
+
+
+import com.jeroenwijering.feeds.AbstractParser;
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.feeds.ATOMParser extends AbstractParser {
+
+
+	/** Contructor **/
+	function ATOMParser() { super(); };
+
+
+	/** build an array with all regular elements **/
+	private function setElements() {
+		elements = new Object();
+		elements["title"] = "title";
+		elements["id"] = "id";
+	};
+
+
+	/** Convert ATOM structure to array **/
+	private function parse(xml:XML):Array {
+		var arr = new Array();
+		var tpl = xml.firstChild.firstChild;
+		var ttl;
+		while(tpl != null) {
+			if (tpl.nodeName.toLowerCase() == "entry") {
+				var obj = new Object();
+				for(var j=0; j<tpl.childNodes.length; j++) {
+					var nod:XMLNode = tpl.childNodes[j];
+					var nnm = nod.nodeName.toLowerCase();
+					if(elements[nnm] != undefined) {
+						obj[elements[nnm]]=nod.firstChild.nodeValue;
+					} else if(nnm=="link" && nod.attributes.rel=="alternate"){
+						obj["link"] =  nod.attributes.href;
+					} else if(nnm == "summary") {
+						obj["description"] = StringMagic.stripTagsBreaks(
+							nod.firstChild.nodeValue);
+					} else if(nnm == "published") {
+						obj["date"] = iso2Date(nod.firstChild.nodeValue);
+					} else if(nnm == "updated") {
+						obj["date"] = iso2Date(nod.firstChild.nodeValue);
+					} else if(nnm == "modified") {
+						obj["date"] = iso2Date(nod.firstChild.nodeValue);
+					} else if(nnm == "category") {
+						obj["category"] = nod.attributes.term;
+					} else if(nnm == "author") { 
+						for(var k=0; k< nod.childNodes.length; k++) {
+							if(nod.childNodes[k].nodeName == "name") {
+								obj["author"] = 
+									nod.childNodes[k].firstChild.nodeValue;
+							}
+						}
+					} else if(nnm=="link" && nod.attributes.rel=="enclosure"){
+						var typ = nod.attributes.type.toLowerCase();
+						if(mimetypes[typ] != undefined){
+							obj["file"] = nod.attributes.href;
+							obj["type"] = mimetypes[typ];
+							if(obj["file"].substr(0,4) == "rtmp") {
+								obj["type"] = "rtmp";
+							}
+						} else if(obj["type"] != undefined && typ == "video/x-flv") {
+							obj["fallback"] = nod.attributes.href;
+						}
+					} else if (nnm=="link" && nod.attributes.rel=="captions"){
+						obj["captions"] = nod.attributes.href;
+					} else if (nnm=="link" && nod.attributes.rel=="audio"){
+						obj["audio"] = nod.attributes.href;
+					} else if (nnm=="link" && nod.attributes.rel=="image"){
+						obj["image"] = nod.attributes.href;
+					}
+				}
+				obj["author"] == undefined ? obj["author"] = ttl: null;
+				arr.push(obj);
+			} else if (tpl.nodeName == "title") { 
+				ttl = tpl.firstChild.nodeValue;
+			}
+			tpl = tpl.nextSibling;
+		}
+		return arr;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/XSPFParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/XSPFParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/XSPFParser.as (revision 1)
@@ -0,0 +1,77 @@
+﻿/**
+* Parses ATOM feeds and returns an indexed array with all elements.
+*
+* @author	Jeroen Wijering
+* @version	1.5
+**/
+
+
+import com.jeroenwijering.feeds.AbstractParser;
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.feeds.XSPFParser extends AbstractParser {
+
+
+	/** Contructor **/
+	function XSPFParser() { super(); };
+
+
+	/** build an array with all regular elements **/
+	private function setElements() {
+		elements = new Object();
+		elements["title"] = "title";
+		elements["creator"] = "author";
+		elements["info"] = "link";
+		elements["image"] = "image";
+		elements["identifier"] = "id";
+		elements["album"] = "category";
+	};
+
+
+	/** Convert ATOM structure to array **/
+	private function parse(xml:XML):Array {
+		var arr = new Array();
+		var tpl = xml.firstChild.firstChild;
+		while(tpl != null) { 
+			if (tpl.nodeName == 'trackList') {
+				for(var i=0; i<tpl.childNodes.length; i++) {
+					var obj = new Object();
+					for(var j=0; j<tpl.childNodes[i].childNodes.length; j++) {
+						var nod:XMLNode = tpl.childNodes[i].childNodes[j];
+						var nnm = nod.nodeName.toLowerCase();
+						if(elements[nnm]!=undefined) {
+							obj[elements[nnm]] = nod.firstChild.nodeValue;
+						} else if(nnm == "location"  && obj['type']!="flv") {
+							obj["file"] = nod.firstChild.nodeValue;
+							var typ = obj["file"].substr(-3).toLowerCase();
+							if(obj["file"].substr(0,4) == "rtmp") {
+								obj["type"] = "rtmp";
+							} else if(obj['file'].indexOf('youtube.com') > -1) {
+								obj["type"] = "youtube";
+							} else if(mimetypes[typ] != undefined) {
+								obj["type"] = mimetypes[typ];
+							}
+						} else if(nnm == "annotation") {
+							obj["description"] = StringMagic.stripTagsBreaks(
+								nod.firstChild.nodeValue);
+						} else if(nnm == "link" && 
+							nod.attributes.rel == "captions") {
+							obj["captions"] = nod.firstChild.nodeValue;
+						} else if(nnm == "link" && 
+							nod.attributes.rel == "audio") {
+							obj["audio"] = nod.firstChild.nodeValue;
+						} else if(nnm == "meta") {
+							obj[nod.attributes.rel] = nod.firstChild.nodeValue;
+						}
+					}
+					arr.push(obj);
+				}
+			}
+			tpl = tpl.nextSibling;
+		}
+		return arr;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/FeedManager.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/FeedManager.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/FeedManager.as (revision 1)
@@ -0,0 +1,259 @@
+﻿/**
+* Parses RSS, ATOM and XSPF lists and returns them as a numerical array.
+*
+* @author	Jeroen Wijering
+* @version	1.7
+**/
+
+
+import com.jeroenwijering.feeds.*;
+
+
+class com.jeroenwijering.feeds.FeedManager {
+
+
+	/** The array the XML is parsed into. **/
+	public var feed:Array;
+	/** XML file **/
+	private var feedXML:XML;
+	/** Flag for captions. **/
+	public var captions:Boolean;
+	/** Flag for extra audiotrack. **/
+	public var audio:Boolean;
+	/** Flag for all items in mp3 **/
+	public var onlymp3s:Boolean;
+	/** Flag for chapter index **/
+	public var ischapters:Boolean;
+	/** Flag for enclosures **/
+	private var enclosures:Boolean;
+	/** Reference to the parser object **/
+	private var parser:AbstractParser;
+	/** An array with objects listening to feed updates **/
+	private var listeners:Array;
+	/** A prefix string for all files **/
+	private var prefix:String = "";
+	/** Stream to use **/
+	private var stream:String;
+	/** Array with all file elements **/
+	private var elements:Object = {
+		file:"",
+		fallback:"",
+		title:"",
+		link:"",
+		id:"",
+		image:"",
+		author:"",
+		captions:"",
+		audio:"",
+		category:"",
+		start:"",
+		type:"",
+		duration:""
+	};
+	/** array with all supported filetypes **/
+	private var filetypes:Array = new Array(
+		"flv","mp3","rbs","jpg","gif","png","rtmp",
+		"swf","mp4","m4v","m4a","mov","3gp","3g2"
+	);
+
+
+	/** Constructor. **/
+	function FeedManager(enc:Boolean,jvs:String,pre:String,str:String) {
+		enc == true ? enclosures = true: enclosures = false;
+		if(jvs == "true") { enableJavascript(); }
+		pre == undefined ? null: prefix = pre;
+		str == undefined ? null: stream = "_"+str;
+		listeners = new Array();
+	};
+
+
+	/** Enable javascript access to loadFile command.  **/
+	private function enableJavascript() {
+		if(flash.external.ExternalInterface.available) {
+			flash.external.ExternalInterface.addCallback(
+				"loadFile",this,loadFile);
+			flash.external.ExternalInterface.addCallback(
+				"addItem",this,addItem);
+			flash.external.ExternalInterface.addCallback(
+				"removeItem",this,removeItem);
+			flash.external.ExternalInterface.addCallback(
+				"itemData",this,itemData);
+			flash.external.ExternalInterface.addCallback(
+				"getLength",this,getLength);
+		}
+	};
+
+
+	/** Load an XML playlist or single media file. **/
+	public function loadFile(obj:Object) {
+		feed = new Array();
+		var ftp = "xml";
+		for(var i = filetypes.length; --i >= 0;) {
+			if(obj['file'].substr(0,4).toLowerCase() == "rtmp") {
+				ftp = "rtmp";
+			} else if(obj['file'].indexOf('youtube.com') > -1) {
+				ftp = "youtube";
+			} else if(obj['type'] == filetypes[i]) {
+				ftp = filetypes[i]; 
+			} else if (obj['file'].substr(-3).toLowerCase() == filetypes[i]) {
+				ftp = filetypes[i]; 
+			}
+		}
+		if (ftp == "xml") {
+			loadXML(unescape(obj['file']));
+		} else {
+			feed[0] = new Object();
+			feed[0]['type'] = ftp;
+			for(var itm in elements) {
+				if(obj[itm] != undefined) {
+					feed[0][itm] = obj[itm];
+				}
+			}
+			playersPostProcess();
+		}
+	};
+
+
+	/** Parse an XML file, return the array when done. **/
+	private function loadXML(url:String) {
+		var ref = this;
+		feedXML = new XML();
+		feedXML.ignoreWhite = true;
+		feedXML.onLoad = function(scs:Boolean) {
+			if(scs) {
+				var fmt = this.firstChild.nodeName.toLowerCase();
+				if( fmt == 'rss') {
+					ref.parser = new RSSParser(ref.prefix);
+					ref.feed = ref.parser.parse(this);
+				} else if (fmt == 'feed') { 
+					ref.parser = new ATOMParser(ref.prefix);
+					ref.feed = ref.parser.parse(this);
+				} else if (fmt == 'playlist') { 
+					ref.parser = new XSPFParser(ref.prefix);
+					ref.feed = ref.parser.parse(this);
+				} else if (fmt == 'asx') { 
+					ref.parser = new ASXParser(ref.prefix);
+					ref.feed = ref.parser.parse(this);
+				} else if (fmt == 'videolist') { 
+					ref.parser = new AgriyaParser(ref.prefix);
+					ref.feed = ref.parser.parse(this);
+				}
+				if(_root.audio != undefined) {
+					ref.feed[0]["audio"] = unescape(_root.audio);
+				}
+				ref.playersPostProcess(url);
+			}
+		};
+		if(_root._url.indexOf("file://") > -1) { feedXML.load(url); } 
+		else if(url.indexOf('?') > -1) { feedXML.load(url+'&'+random(999)); }
+		else { feedXML.load(url+'?'+random(999)); }
+	};
+
+
+	/** set a number of flags specifically used by the players **/
+	private function playersPostProcess(url:String) {
+		onlymp3s = true;
+		feed.length > 1 ? ischapters = true: ischapters = false;
+		captions = false;
+		audio = false;
+		for(var i=0; i<feed.length; i++) {
+			feed[i]["file"] = prefix+feed[i]["file"];
+			if(feed[i]["image"]) { feed[i]["image"] = prefix+feed[i]["image"]; }
+			if(feed[i]["link"]) { feed[i]["link"] = prefix+feed[i]["link"]; }
+			if(stream != undefined) {
+				if(feed[i]["type"] == "rtmp") {
+					feed[i]["id"] += stream;
+					feed[i]["file"] = feed[i]["file"];
+				} else if(feed[i]["type"] == "flv") {
+					feed[i]["file"] = 
+						feed[i]["file"].substr(0,feed[i]["file"].length-4) + 
+						stream + feed[i]["file"].substr(-4);
+				}
+			}
+			if(feed[i]["type"] != "mp3") { onlymp3s = false; }
+			if(feed[i]["start"] == undefined) { feed[i]["start"] = 0; }
+			if(feed[i]['file'] != feed[0]['file']) { ischapters = false; }
+			if(feed[i]["captions"] != undefined) { captions = true; }
+			if(feed[i]["audio"] != undefined) { audio = true; }
+			if(feed[i]['duration'] == undefined || isNaN(feed[i]['duration'])){
+				feed[i]['duration'] = 0; 
+			}
+			if(feed[i]['fallback'] != undefined) {
+				var maj = Number(System.capabilities.version.split(' ')[1].substr(0,1));
+				var min = Number(System.capabilities.version.split(',')[2]);
+				if(maj < 9 || (maj == 9 && min < 90)) {
+					feed[i]['file'] = feed[i]['fallback'];
+				}
+			}
+		}
+		updateListeners('new');
+	}
+
+
+	/** Return the lenght of the feed array. **/
+	public function getLength():Number {
+		return feed.length;
+	}
+
+
+	/** Add an item to the feed **/
+	public function addItem(obj:Object,idx:Number) {
+		if(obj['title'] == undefined) { obj['title'] = obj['file']; }
+		if(obj['type'] == undefined) { obj['type'] = obj['file'].substr(-3); }
+		if(arguments.length == 1 || idx >= feed.length) {
+			feed.push(obj);
+		} else {
+			var arr1 = feed.slice(0,idx);
+			var arr2 = feed.slice(idx);
+			arr1.push(obj);
+			feed = arr1.concat(arr2);
+		}
+		updateListeners('add');
+	};
+
+
+	/** Remove an item from the feed **/
+	public function removeItem(idx:Number) {
+		if(feed.length == 1) {
+			return;
+		} else  if(arguments.length == 0 || idx >= feed.length) {
+			feed.pop();
+		} else {
+			feed.splice(idx,1);
+		}
+		updateListeners('remove');
+	};
+
+
+	/** Retrieve playlist data for a specific item **/
+	public function itemData(idx:Number):Object {
+		return feed[idx];
+	};
+
+
+	/** Add a feed update listener. **/
+	public function addListener(lst:Object) {
+		listeners.push(lst);
+	};
+
+
+	/** Remove a feed update listener. **/
+	public function removeListener(lst:Object) {
+		for(var i = listeners.length; --i >= 0; ) {
+			if(listeners[i] == lst) {
+				listeners.splice(i,1);
+				return;
+			}
+		}
+	};
+
+
+	/** Notify all listeners of a feed update **/
+	private function updateListeners(typ:String) {
+		for(var i = listeners.length; --i >= 0; ) {
+			listeners[i].onFeedUpdate(typ);
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/AbstractParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/AbstractParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/AbstractParser.as (revision 1)
@@ -0,0 +1,143 @@
+﻿/**
+* General functionality of all feedtype-parsers.
+*
+* @author	Jeroen Wijering
+* @version	1.3
+**/
+
+
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.feeds.AbstractParser {
+
+
+	/** All elements that can be parsed without manipulations **/
+	private var elements:Object;
+	/** Accepted mimetypes for enclosures **/
+	private var mimetypes:Object;
+	/** Timezone abbreviation offsets **/
+	private var timezones:Object = { IDLW:-12,NT:-11,AHST:-10,CAT:-10,HST:-10,
+		YST:-9,PST:-8,MST:-7,PDT:-7,CST:-6,EST:-5,CDT:-5,EDT:-4,ADT:-3,WBT:-4,
+		AST:-4,NT:-3.5,EBT:-3,AT:-2,WAT:-1,UTC:0,UT:0,GMT:0,WET:0,CET:1,
+		CEST:1,EET:2,EEDT:3,MSK:3,IRT:3.5,SAMT:4,YEKT:5,TMT:5,TJT:5,OMST:6,
+		NOVT:6,LKT:6,MMT:6.5,KRAT:7,ICT:7,WIT:7,WAST:7,IRKT:8,ULAT:8,CST:8,
+		CIT:8,BNT:8,YAKT:9,JST:9,KST:9,EIT:9,ACST:9.5,VLAT:10,ACDT:10.5,
+		SAKT:10,GST:10,MAGT:11,IDLE:12,PETT:12,NZST:12
+	};	
+	/** Supporting array to translate RFC2822 months to number. **/
+	private var MONTH_INDEXES:Object = {January:0,February:1,March:2,April:3,
+		May:4,June:5,July:6,August:7,September:8,October:9,November:10,
+		December:11,Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,
+		Oct:9,Nov:10,Dec:11};
+
+
+	/** Constructor. **/
+	function AbstractParser(pre:String) {
+		setElements();
+		setMimes();
+	};
+
+
+	/** build an array with all regular elements **/
+	private function setElements() {
+		elements = new Object();
+	};
+
+
+	/** build an array with all registered mimetypes **/
+	private function setMimes() {
+		mimetypes = new Object();
+		mimetypes["mp3"] = "mp3";
+		mimetypes["audio/mpeg"] = "mp3";
+		mimetypes["flv"] = "flv";
+		mimetypes["video/x-flv"] = "flv";
+		mimetypes["jpeg"] = "jpg";
+		mimetypes["jpg"] = "jpg";
+		mimetypes["image/jpeg"] = "jpg";
+		mimetypes["png"] = "png";
+		mimetypes["image/png"] = "png";
+		mimetypes["gif"] = "gif";
+		mimetypes["image/gif"] = "gif";
+		mimetypes["rtmp"] = "rtmp";
+		mimetypes["swf"] = "swf";
+		mimetypes["mov"] = "mov";
+		mimetypes["application/x-shockwave-flash"] = "swf";
+		mimetypes["rtmp"] = "rtmp";
+		mimetypes["application/x-fcs"] = "rtmp";
+		mimetypes["audio/x-m4a"] = "m4a";
+		mimetypes["audio/x-mp4"] = "mp4";
+		mimetypes["video/x-m4v"] = "m4v";
+		mimetypes["video/mp4"] = "mp4";
+		mimetypes["video/h264"] = "mp4";
+		mimetypes["video/3gpp"] = "3gp";
+		mimetypes["video/x-3gpp2"] = "3g2";
+		mimetypes["audio/x-3gpp2"] = "3g2";
+	};
+
+
+	/** Parse a specific object. **/
+	function parse(xml:XML):Array {
+		var arr:Array = new Array();
+		for(var i=0; i<xml.firstChild.childNodes.length; i++) {
+			arr.push(xml.firstChild.childNodes[i].nodeName);
+		}
+		return arr;
+	};
+
+
+	/** Translate RFC2822 date strings to timestamp. **/
+	private function rfc2Date(dat:String):Number {
+		if(isNaN(dat)) {
+			var darr:Array = dat.split(' ');
+			darr[1] == "" ? darr.splice(1,1) : null;
+			var month:Number = MONTH_INDEXES[darr[2]];
+			var date:Number = darr[1].substring(0,2);
+			var year:Number = darr[3];
+			var zone = darr[5];
+			var tarr = darr[4].split(':');
+			var myDate = new Date(year,month,date,tarr[0],tarr[1],tarr[2]);
+			var stamp = Math.round(myDate.valueOf()/1000) - 
+				myDate.getTimezoneOffset()*60;
+			if(isNaN(zone)) { 
+				stamp -= 3600*timezones[zone]; 
+			} else { 
+				stamp -= 3600*Number(zone.substring(0,3)) - 
+					60*Number(zone.substring(3,2));
+			}
+			return stamp;
+		} else {
+			return Number(dat);
+		}
+	};
+
+
+	/** Translate ISO8601 date strings to timestamp. **/
+	private function iso2Date(dat):Number {
+		if(isNaN(dat)) {
+			while(dat.indexOf(" ") > -1) {
+				var idx = dat.indexOf(" ");
+				dat = dat.substr(0,idx) + dat.substr(idx+1);
+			}
+			var myDate = new Date(dat.substr(0,4),dat.substr(5,2)-1,
+				dat.substr(8,2),dat.substr(11,2),dat.substr(14,2),
+				dat.substr(17,2));
+			var stamp = Math.round(myDate.valueOf()/1000) - 
+				myDate.getTimezoneOffset()*60;
+			if(dat.length > 20) { 
+				var hr:Number = Number(dat.substr(20,2));
+				var mn:Number = Number(dat.substr(23,2));
+				if(dat.charAt(19) == "-") {
+					stamp = stamp - hr*3600 - mn*60;
+				} else {
+					stamp += hr*3600 + mn*60;
+				}
+			}
+			return stamp;
+		} else {
+			return dat;
+		}
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/RSSParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/RSSParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/RSSParser.as (revision 1)
@@ -0,0 +1,132 @@
+﻿/**
+* Parses ATOM feeds and returns an indexed array with all elements
+*
+* @author	Jeroen Wijering
+* @version	1.5
+**/
+
+
+import com.jeroenwijering.feeds.AbstractParser;
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.feeds.RSSParser extends AbstractParser {
+
+
+	/** Contructor **/
+	function RSSParser() { super(); };
+
+
+	/** build an array with all regular elements **/
+	private function setElements() {
+		elements = new Object();
+		elements["title"] = "title";
+		elements["guid"] = "id";
+		elements["category"] = "category";
+		elements["link"] = "link";
+		elements["geo:lat"] = "latitude";
+		elements["geo:long"] = "longitude";
+		elements["geo:city"] = "city";
+	};
+
+
+	/** Convert RSS structure to array **/
+	private function parse(xml:XML):Array {
+		var arr = new Array();
+		var tpl = xml.firstChild.firstChild.firstChild;
+		var ttl;
+		while(tpl != null) {
+			if (tpl.nodeName.toLowerCase() == "item") {
+				var obj = new Object();
+				for(var j=0; j<tpl.childNodes.length; j++) {
+					var nod:XMLNode = tpl.childNodes[j];
+					var nnm = nod.nodeName.toLowerCase();
+					if(elements[nnm] != undefined) {
+						obj[elements[nnm]] = nod.firstChild.nodeValue;
+					} else if(nnm == "description") {
+						obj["description"] = StringMagic.stripTagsBreaks(
+							nod.firstChild.nodeValue);
+					} else if(nnm == "pubdate") {
+						obj["date"] = rfc2Date(nod.firstChild.nodeValue);
+					} else if(nnm == "dc:date") {
+						obj["date"] = iso2Date(nod.firstChild.nodeValue);
+					} else if(nnm == "media:credit") {
+						obj["author"] = nod.firstChild.nodeValue;
+					} else if(nnm == "media:thumbnail") {
+						obj["image"] = nod.attributes.url;
+					} else if(nnm == "itunes:image") {
+						obj["image"] = nod.attributes.href;
+					} else if(nnm == "georss:point") {
+						var gpt = nod.firstChild.nodeValue.split(" ");
+						obj["latitude"] = Number(gpt[0]);
+						obj["longitude"] = Number(gpt[1]);
+					} else if(nnm == "enclosure" || nnm == "media:content") {
+						var typ = nod.attributes.type.toLowerCase();
+						if(mimetypes[typ]!=undefined && obj["type"] == undefined) {
+							obj["type"] = mimetypes[typ];
+							obj['file'] = nod.attributes.url;
+							obj['duration'] = 
+								StringMagic.toSeconds(nod.attributes.duration);
+							if(obj["file"].substr(0,4) == "rtmp") {
+								obj["type"] = "rtmp";
+							} else if(obj['file'].indexOf('youtube.com') > -1) {
+								obj["type"] = "youtube";
+							}
+							if(nod.childNodes[0].nodeName=="media:thumbnail"){
+								obj["image"]=nod.childNodes[0].attributes.url;
+							}
+						} else if(obj["type"] != undefined && typ == "video/x-flv") {
+							obj['fallback'] = nod.attributes.url;
+						} else if(typ == "captions") {
+							obj["captions"] = nod.attributes.url;
+						} else if(typ == "audio") {
+							obj["audio"] = nod.attributes.url;
+						}
+					} else if(nnm == "media:group") { 
+						for(var k=0; k< nod.childNodes.length; k++) {
+							var ncn=nod.childNodes[k].nodeName.toLowerCase();
+							if(ncn == "media:content") {
+								var ftp = nod.childNodes[k].attributes.type.toLowerCase();
+								if(mimetypes[ftp] != undefined && obj["type"] == undefined) {
+									obj["file"] = nod.childNodes[k].attributes.url;
+									obj['duration'] = StringMagic.toSeconds(
+										nod.attributes.duration);
+									obj["type"]=mimetypes[ftp];
+									if(obj["file"].substr(0,4) == "rtmp") {
+										obj["type"] = "rtmp";
+									} else if(obj['file'].indexOf('youtube.com') > -1) {
+										obj["type"] = "youtube";
+									}
+								} 
+								if(obj["type"] != undefined && ftp == "video/x-flv") {
+									obj['fallback'] = nod.childNodes[k].attributes.url;
+								}
+							}
+							if(ncn == "media:thumbnail") {
+								obj["image"]=nod.childNodes[k].attributes.url;
+							}
+							if(ncn == "media:credit") {
+								obj["author"]=nod.childNodes[k].firstChild.nodeValue;
+							}
+						}
+					}
+				}
+				if(obj["image"] == undefined) {
+					if(obj["file"].indexOf(".jpg") > 0 || 
+						obj["file"].indexOf(".png") > 0 || 
+						obj["file"].indexOf(".gif") > 0) {
+						obj["image"] = obj["file"];
+					}
+				}
+				if(obj["author"] == undefined) { obj["author"] = ttl; }
+				arr.push(obj);
+			} else if (tpl.nodeName == "title") {
+				ttl = tpl.firstChild.nodeValue;
+			}
+			tpl = tpl.nextSibling;
+		}
+		return arr;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/ASXParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/ASXParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/ASXParser.as (revision 1)
@@ -0,0 +1,69 @@
+/**
+* Parses ASX feeds and returns an indexed array with all elements
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.feeds.AbstractParser;
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.feeds.ASXParser extends AbstractParser {
+
+
+	/** Contructor **/
+	function ASXParser() { super(); };
+
+
+	/** build an array with all regular elements **/
+	private function setElements() {
+		elements = new Object();
+		elements["title"] = "title";
+		elements["author"] = "author";
+		elements["abstract"] = "description";
+	};
+
+
+	/** Convert RSS structure to array **/
+	private function parse(xml:XML):Array {
+		var arr = new Array();
+		var tpl = xml.firstChild.firstChild;
+		while(tpl != null) {
+			if (tpl.nodeName.toLowerCase() == "entry") {
+				var obj = new Object();
+				for(var j=0; j<tpl.childNodes.length; j++) {
+					var nod:XMLNode = tpl.childNodes[j];
+					var nnm = nod.nodeName.toLowerCase();
+					if(elements[nnm] != undefined) {
+						obj[elements[nnm]] = nod.firstChild.nodeValue;
+					} else if(nnm == "moreinfo") {
+						obj["link"] = nod.attributes.href;
+					} else if(nnm == "duration") {
+						obj["duration"] = 
+							StringMagic.toSeconds(nod.attributes.value);
+					} else if(nnm == "ref") {
+						obj["file"] = nod.attributes.href;
+						var typ = nod.attributes.href.substr(-3);
+						if(mimetypes[typ]!=undefined) {
+							obj["type"] = mimetypes[typ];
+						}
+						if(obj["file"].substr(0,4) == "rtmp") {
+							obj["type"] = "rtmp";
+						} else if(obj['file'].indexOf('youtube.com') > -1) {
+							obj["type"] = "youtube";
+						}
+					} else if(nnm == "param") {
+						obj[nod.attributes.name] = nod.attributes.value;
+					}
+				}
+				arr.push(obj);
+			}
+			tpl = tpl.nextSibling;
+		}
+		return arr;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/AgriyaParser.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/AgriyaParser.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/AgriyaParser.as (revision 1)
@@ -0,0 +1,45 @@
+/**
+* Parses Agriya playlists and returns an indexed array with all elements
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.feeds.AbstractParser;
+import com.jeroenwijering.utils.StringMagic;
+
+
+class com.jeroenwijering.feeds.AgriyaParser extends AbstractParser {
+
+
+	/** Contructor **/
+	function AgriyaParser() { super(); };
+
+
+	/** Convert Agriya structure to array **/
+	private function parse(xml:XML):Array {
+		var arr = new Array();
+		var tpl = xml.firstChild.firstChild.firstChild;
+		while(tpl != null) {
+			if (tpl.nodeName.toLowerCase() == "video") {
+				var obj = new Object();
+				obj['file'] = tpl.attributes.Path;
+				obj['image'] = tpl.attributes.Thumbnail;
+				obj['title'] = tpl.attributes.Description;
+				if(obj["file"].substr(0,4) == "rtmp") {
+					obj["type"] = "rtmp";
+				} else if(obj['file'].indexOf('youtube.com') > -1) {
+					obj["type"] = "youtube";
+				} else { 
+					obj['type'] = obj["file"].substr(-3).toLowerCase();
+				}
+				arr.push(obj);
+			}
+			tpl = tpl.nextSibling;
+		}
+		return arr;
+	};
+
+
+}
Index: /trunk/as2/com/jeroenwijering/feeds/FeedListener.as
===================================================================
--- /trunk/as2/com/jeroenwijering/feeds/FeedListener.as (revision 1)
+++ /trunk/as2/com/jeroenwijering/feeds/FeedListener.as (revision 1)
@@ -0,0 +1,19 @@
+/**
+* Interface for all objects that need real-time feed updates.
+*
+* @author	Jeroen Wijering
+* @version	1.0
+**/
+
+
+import com.jeroenwijering.feeds.*;
+
+
+interface com.jeroenwijering.feeds.FeedListener {
+
+
+	/** invoked when the feed object has updated **/
+	function onFeedUpdate(typ:String);
+
+
+}
Index: /trunk/as3/com/jeroenwijering/parsers/ATOMParser.as
===================================================================
--- /trunk/as3/com/jeroenwijering/parsers/ATOMParser.as (revision 388)
+++ /trunk/as3/com/jeroenwijering/parsers/ATOMParser.as (revision 1)
@@ -5,18 +5,22 @@
 
 
-import com.jeroenwijering.parsers.JWParser;
 import com.jeroenwijering.parsers.MediaParser;
+import com.jeroenwijering.parsers.ObjectParser;
 import com.jeroenwijering.utils.Strings;
 
 
-public class ATOMParser {
+public class ATOMParser extends ObjectParser {
 
 
 	/** Parse an RSS playlist for feeditems. **/
 	public static function parse(dat:XML):Array {
-		var arr:Array = new Array();
-		for each (var i:XML in dat.children()) {
+		var arr = new Array();
+		var itm = new Object();
+		for each (var i in dat.children()) {
 			if (i.localName() == 'entry') {
-				arr.push(ATOMParser.parseItem(i));
+				itm = ATOMParser.parseItem(i);
+			}
+			if(itm['type'] != undefined) {
+				arr.push(itm);
 			}
 		}
@@ -27,7 +31,7 @@
 	/** Translate ATOM item to playlist item. **/
 	public static function parseItem(obj:XML):Object {
-		var itm:Object =  new Object();
-		for each (var i:XML in obj.children()) {
-			switch(i.localName().toLowerCase()) {
+		var itm =  new Object();
+		for each (var i in obj.children()) {
+			switch(i.localName()) {
 				case 'author':
 					itm['author'] = i.children()[0].text().toString();
@@ -41,17 +45,14 @@
 				case 'link':
 					if(i.@rel == 'alternate') {
-						itm['link'] = i.@href.toString();
-					} else if (i.@rel == 'enclosure') {
-						itm['file'] = i.@href.toString();
+						itm['link'] = i.@href;
 					}
 					break;
-				case 'published':
-					itm['date'] = i.text().toString();
+				case 'group':
+					itm = MediaParser.parseGroup(i,itm);
 					break;
 			}
 		}
 		itm = MediaParser.parseGroup(obj,itm);
-		itm = JWParser.parseEntry(obj,itm);
-		return itm;
+		return ObjectParser.detect(itm);
 	};
 
Index: /trunk/as3/com/jeroenwijering/parsers/XSPFParser.as
===================================================================
--- /trunk/as3/com/jeroenwijering/parsers/XSPFParser.as (revision 374)
+++ /trunk/as3/com/jeroenwijering/parsers/XSPFParser.as (revision 1)
@@ -5,19 +5,22 @@
 
 
-import com.jeroenwijering.parsers.JWParser;
-import com.jeroenwijering.parsers.MediaParser;
 import com.jeroenwijering.utils.Strings;
+import com.jeroenwijering.parsers.ObjectParser;
 
 
-public class XSPFParser {
+public class XSPFParser extends ObjectParser {
 
 
 	/** Parse an XSPF playlist for feeditems. **/
 	public static function parse(dat:XML):Array {
-		var arr:Array = new Array();
-		for each (var i:XML in dat.children()) {
+		var arr = new Array();
+		var itm = new Object();
+		for each (var i in dat.children()) {
 			if (i.localName().toLowerCase() == 'tracklist') {
-				for each (var j:XML in i.children()) {
-					arr.push(XSPFParser.parseItem(j));
+				for each (var j in i.children()) {
+					itm = XSPFParser.parseItem(j);
+					if(itm['type'] != undefined) {
+						arr.pus