add allocine player, and add an full test with this allocine player. popcorn-port
authorcavaliet
Tue, 21 Feb 2012 17:26:51 +0100
branchpopcorn-port
changeset 819 9b8e68803f6f
parent 818 1d5816a94943
child 820 7968346b9689
add allocine player, and add an full test with this allocine player.
src/js/players/player.allocine.js
test/integration/allocine_dossier_independant/allocine_test/AcPlayer_v3.0.swf
test/integration/allocine_dossier_independant/allocine_test/avatar.png
test/integration/allocine_dossier_independant/allocine_test/data_cinecast.xml
test/integration/allocine_dossier_independant/allocine_test/exemple.json
test/integration/allocine_dossier_independant/allocine_test/skin_fr.swf
test/integration/allocine_dossier_independant/css/LdtPlayer.css
test/integration/allocine_dossier_independant/css/imgs/annotate_arrow.png
test/integration/allocine_dossier_independant/css/imgs/annotate_sprite.png
test/integration/allocine_dossier_independant/css/imgs/arrow.png
test/integration/allocine_dossier_independant/css/imgs/black.png
test/integration/allocine_dossier_independant/css/imgs/black_arrow.png
test/integration/allocine_dossier_independant/css/imgs/black_arrow_big.png
test/integration/allocine_dossier_independant/css/imgs/black_big.png
test/integration/allocine_dossier_independant/css/imgs/delicious.png
test/integration/allocine_dossier_independant/css/imgs/facebook.png
test/integration/allocine_dossier_independant/css/imgs/facebook_button.png
test/integration/allocine_dossier_independant/css/imgs/google.png
test/integration/allocine_dossier_independant/css/imgs/gplus_button.png
test/integration/allocine_dossier_independant/css/imgs/grey_arrow_Show.png
test/integration/allocine_dossier_independant/css/imgs/left_edge_arrow.png
test/integration/allocine_dossier_independant/css/imgs/left_handle.gif
test/integration/allocine_dossier_independant/css/imgs/linkedin.png
test/integration/allocine_dossier_independant/css/imgs/loader.gif
test/integration/allocine_dossier_independant/css/imgs/loader_fc.gif
test/integration/allocine_dossier_independant/css/imgs/loader_fc2.gif
test/integration/allocine_dossier_independant/css/imgs/minimize.png
test/integration/allocine_dossier_independant/css/imgs/mute_sprite.png
test/integration/allocine_dossier_independant/css/imgs/pause_sprite.png
test/integration/allocine_dossier_independant/css/imgs/play_sprite.png
test/integration/allocine_dossier_independant/css/imgs/player_gradient.png
test/integration/allocine_dossier_independant/css/imgs/polemic_buttons_sprite.png
test/integration/allocine_dossier_independant/css/imgs/profile_arrow.png
test/integration/allocine_dossier_independant/css/imgs/purple_arrow_Show.png
test/integration/allocine_dossier_independant/css/imgs/reply_sprite.png
test/integration/allocine_dossier_independant/css/imgs/retweet_sprite.png
test/integration/allocine_dossier_independant/css/imgs/right_edge_arrow.png
test/integration/allocine_dossier_independant/css/imgs/right_handle.gif
test/integration/allocine_dossier_independant/css/imgs/search_sprite.png
test/integration/allocine_dossier_independant/css/imgs/sound_sprite.png
test/integration/allocine_dossier_independant/css/imgs/spinner.gif
test/integration/allocine_dossier_independant/css/imgs/submit_annotation.png
test/integration/allocine_dossier_independant/css/imgs/transBlack.png
test/integration/allocine_dossier_independant/css/imgs/tweet_button.png
test/integration/allocine_dossier_independant/css/imgs/twitter.png
test/integration/allocine_dossier_independant/css/imgs/user_default_icon.png
test/integration/allocine_dossier_independant/css/imgs/video_sequence.png
test/integration/allocine_dossier_independant/css/imgs/white.png
test/integration/allocine_dossier_independant/css/imgs/white_arrow.png
test/integration/allocine_dossier_independant/css/imgs/white_arrow_big.png
test/integration/allocine_dossier_independant/css/imgs/white_arrow_long.png
test/integration/allocine_dossier_independant/css/imgs/white_arrow_mini.png
test/integration/allocine_dossier_independant/css/imgs/white_big.png
test/integration/allocine_dossier_independant/css/imgs/widget20.png
test/integration/allocine_dossier_independant/css/imgs/wire_pattern.png
test/integration/allocine_dossier_independant/js/LdtPlayer-release.js
test/integration/allocine_dossier_independant/js/libs/jquery.sparkline.js
test/integration/allocine_dossier_independant/js/libs/jwplayer.js
test/integration/allocine_dossier_independant/js/libs/lab.js
test/integration/allocine_dossier_independant/js/libs/mustache.js
test/integration/allocine_dossier_independant/js/libs/popcorn.code.js
test/integration/allocine_dossier_independant/js/libs/popcorn.js
test/integration/allocine_dossier_independant/js/libs/popcorn.jwplayer.js
test/integration/allocine_dossier_independant/js/libs/popcorn.youtube.js
test/integration/allocine_dossier_independant/js/libs/raphael.js
test/integration/allocine_dossier_independant/js/libs/underscore.js
test/integration/allocine_dossier_independant/polemic-allocine.htm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/js/players/player.allocine.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,122 @@
+/* To wrap a player the develop should create a new class derived from
+the IriSP.PopcornReplacement.player and defining the correct functions */
+
+/** allocine player wrapper */
+IriSP.PopcornReplacement.allocine = function(container, options) {
+    console.log("Calling allocine player");
+    /* appel du parent pour initialiser les structures communes à tous les players */
+    IriSP.PopcornReplacement.player.call(this, container, options);   
+    
+    var _this = this;
+
+    /* Définition des fonctions de l'API -  */
+
+    this.playerFns = {
+        play : function() {
+            if (_this.player) {
+                console.log("ask play _this.player = " + _this.player);
+                return _this.player.sendToActionScript("play");
+            } else {
+                return false;
+            }
+        },
+        pause : function() {
+            if (_this.player) {
+                console.log("ask pause _this.player = " + _this.player);
+                return _this.player.sendToActionScript("pause");
+            } else {
+                return false;
+            }
+        },
+        getPosition : function() {
+            if (_this.player) {
+                return _this.player.sendToActionScript("getSeek","return");
+            } else {
+                return 0;
+            }
+        },
+        seek : function(pos) {
+            if (_this.player) {
+                return _this.player.sendToActionScript("seek",pos);
+            } else {
+                return false;
+            }
+        },
+        getMute : function() {
+            if (_this.player) {
+                return _this.player.sendToActionScript("getMute","return");
+            } else {
+                return false;
+            }
+        },
+        setMute : function(p) {
+            if (_this.player) {
+                //return p ? _this.player.sendToActionScript("setMute") : _this.player.sendToActionScript("setMute");
+            	_this.player.sendToActionScript("setMute");
+            } else {
+                return false;
+            }
+        }
+    }
+
+    window.onReady = IriSP.wrap(this, this.ready);
+    //NOT CALLED window.onAllocineStateChange = IriSP.wrap(this, this.stateHandler);
+    window.onTime = IriSP.wrap(this, this.progressHandler);
+    
+    var fv = "adVast=false&lg=fr_cinecast&autoPlay="+options.autoPlay+"&directVideoTitle= &urlAcData="+options.urlAcData+"&directVideoPath="+options.directVideoPath+"&host=http://allocine.fr";
+    console.log("fv = " + fv);
+    
+    var params = {
+        "allowScriptAccess" : "always",
+        "wmode": "opaque",
+        "flashvars" : fv
+    };
+    var atts = {
+        id : this.container
+    };
+    swfobject.embedSWF(options.acPlayerUrl, this.container, options.width, options.height, "8", null, null, params, atts);
+
+};
+
+IriSP.PopcornReplacement.allocine.prototype = new IriSP.PopcornReplacement.player("", {});
+
+IriSP.PopcornReplacement.allocine.prototype.ready = function() {
+    this.player = document.getElementById(this.container);
+    this.player.addEventListener("onStateChange", "onAllocineStateChange");
+    this.player.addEventListener("onVideoProgress", "onAllocineVideoProgress");
+    this.player.cueVideoByUrl(this._options.video);
+    this.callbacks.onReady();
+};
+
+IriSP.PopcornReplacement.allocine.prototype.progressHandler = function(progressInfo) {
+    this.callbacks.onTime({
+        position: progressInfo.mediaTime
+    });
+}
+
+
+IriSP.PopcornReplacement.allocine.prototype.stateHandler = function(state) {
+    
+    switch(state) {
+        case 1:
+            this.callbacks.onPlay();
+            break;
+
+        case 2:
+            this.callbacks.onPause();
+            break;
+
+        case 3:
+            this.callbacks.onSeek({
+                position: this.player.getCurrentTime()
+            });
+            break;
+
+        /*
+        case 5:
+            this.callbacks.onReady();
+            break;
+        */
+    }
+    
+};
\ No newline at end of file
Binary file test/integration/allocine_dossier_independant/allocine_test/AcPlayer_v3.0.swf has changed
Binary file test/integration/allocine_dossier_independant/allocine_test/avatar.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/allocine_test/data_cinecast.xml	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,197 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<AcVision>
+<AcVisionVideo refType="mediatheme"
+ref="18644642"
+refTitle="La Minute"
+cmedia="19299288"
+videoTitle="La Minute N°1033 - Mardi 17 janvier 2012"
+mediaType="show"
+genre=""
+urlGen="/video/emissions/laminute/episode/?cmedia=19299288"
+ld_path="http://b.fr.mediaplayer.allocine.fr/nmedia/18/64/46/42/19299288_minute_2012_01_17_l_001.flv"
+md_path="http://h.fr.mediaplayer.allocine.fr/nmedia/18/64/46/42/19299288_minute_2012_01_17_m_001.flv"
+hd_path="http://hd.fr.mediaplayer.allocine.fr/nmedia/18/64/46/42/19299288_minute_2012_01_17_sd_001.flv"
+xt_title="19299288_la_minute_emission_allocinetv"
+thumb=""
+urlFiche="laminute_home"
+/>
+<AcVisionVideoSlides>
+<AcVisionVideoSlide title="La Minute N°1043 - Mardi 31 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19303868_minute_2012_01_31.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19303868" />
+<AcVisionVideoSlide title="La Minute N°1042 - Lundi 30 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19303866_minute_2012_01_30.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19303866" />
+<AcVisionVideoSlide title="La Minute N°1041 - Samedi 28 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19303864_minute_2012_01_28.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19303864" />
+<AcVisionVideoSlide title="La Minute N°1040 - Jeudi 26 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19303192_minute_2012_01_26.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19303192" />
+<AcVisionVideoSlide title="La Minute N°1039 - Mercredi 25 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19302927_minute_2012_01_25.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19302927" />
+<AcVisionVideoSlide title="La Minute N°1038 - Mardi 24 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19300902_minute_2012_01_24.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19300902" />
+<AcVisionVideoSlide title="La Minute N°1037 - Lundi 23 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19300590_minute_2012_01_23.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19300590" />
+<AcVisionVideoSlide title="La Minute N°1036 - Vendredi 20 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19300162_minute_2012_01_20.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19300162" />
+<AcVisionVideoSlide title="La Minute N°1035 - Jeudi 19 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19299668_minute_2012_01_19.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19299668" />
+<AcVisionVideoSlide title="La Minute N°1034 - Mercredi 18 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19299358_minute_2012_01_18.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19299358" />
+<AcVisionVideoSlide title="La Minute N°1032 - Lundi 16 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19298693_minute_2012_01_16.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19298693" />
+<AcVisionVideoSlide title="La Minute N°1031 - Vendredi 13 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19298503_minute_2012_01_13.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19298503" />
+<AcVisionVideoSlide title="La Minute N°1030 - Jeudi 12 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19298131_minute_2012_01_12.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19298131" />
+
+<AcVisionVideoSlide title="La Minute N°1029 - Mercredi 11 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19296197_minute_2012_01_11.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19296197" />
+<AcVisionVideoSlide title="La Minute N°1028 - Mardi 10 janvier 2012"
+thumb="/c_100_80/medias/nmedia/18/64/46/42/19290943_minute_2012_01_10.jpg"
+link="/video/emissions/laminute/episode/?cmedia=19290943" />
+</AcVisionVideoSlides>
+<AcVisionSkin skinSource="allocine_test/skin_fr.swf">
+<clip nameId="titleTopPostRoll">
+<label>
+<![CDATA[AlloCiné présente: | Cliquez ici pour en savoir plus]]>
+</label>
+</clip>
+<clip nameId="mcAddPostRoll" layer1="McAddPostRoll" x="0" y="0">
+<label>
+<![CDATA[Vidéo suivante dans|secondes]]>
+</label>
+</clip>
+<clip nameId="bgAddPostRoll" layer1="BgAddPostRoll" y="20"/>
+<clip nameId="pixAddPostRoll" y="20"/>
+<clip nameId="titleAddPostRoll" y="30" x="100"/>
+
+<clip nameId="accountAddPostRoll" x="10" y="2"/>
+<clip nameId="fondNavPlayer" layer1="FondNavPlayer" width="760" height="36"/>
+<clip nameId="soundVolume" x="77" y="13" width="36" height="36" layer3="SoundVolumeLayer1" layer2="SoundVolumeLayer2" layer1="SoundVolumeLayer3">
+<info>
+<![CDATA[Modifier le volume de la vidéo]]>
+</info>
+</clip>
+<clip nameId="cutSound" x="55" y="13" stateOn="BtCutSoundOn" stateOff="BtCutSoundOff" stateClickOff="BtCutSoundClickOff" stateClickOn="BtCutSoundClickOn">
+<info>
+<![CDATA[Couper le son de la vidéo]]>
+</info>
+</clip>
+<clip nameId="puceTimeLine" stateOn="PuceTimeLineOn" stateOff="PuceTimeLineOff"/>
+<clip nameId="btPlay" x="4" y="11" width="42" height="22" stateOn="BtPlayOn" stateOff="BtPlayOff"/>
+<clip nameId="btPause" x="4" y="11" width="42" height="22" stateOn="BtPauseOn" stateOff="BtPauseOff"/>
+<clip nameId="btReplay2" x="4" y="11" width="42" height="22" stateOn="BtReplay2On" stateOff="BtReplay2Off" />
+<clip nameId="btBigPlay" stateOn="BtBigPlayOn" stateOff="BtBigPlayOff"/>
+
+<clip nameId="btHd" x="553" y="11" stateOn="BtHdOn" stateOff="BtHdOff" stateClickOn="BtHdClickOn">
+<info>
+<![CDATA[Changer la qualité de la vidéo]]>
+</info>
+</clip>
+<clip nameId="btMenu" x="626" y="11" stateOn="BtMenuOn" stateOff="BtMenuOff"/>
+<clip nameId="btMarque" link="www.allocine.fr" linkTarget="_blank" x="688" y="16" stateOn="BtMarqueOn" stateOff="BtMarqueOff">
+<info>
+<![CDATA[Se rendre sur allocine.fr]]>
+</info>
+</clip>
+<clip nameId="btFullScreen" x="593" y="11" stateOn="BtFullScreenOn" stateOff="BtFullScreenOff" stateClickOn="BtFullScreenClickOn" stateClickOff="BtFullScreenClickOff">
+<info>
+<![CDATA[Mettre la vidéo en plein écran]]>
+</info>
+</clip>
+<clip nameId="textTimer" x="120" y="13" color="0x444444"/>
+
+<clip nameId="menuSubtitle" x="330" y="-2" color="0x9ea6b8"/>
+<clip nameId="menuFlow" x="230" y="-2" color="0x9ea6b8"/>
+<clip nameId="textTitle" x="215" y="13" color="0x005ea8" />
+<clip nameId="btClose" stateOn="BtCloseOn" stateOff="BtCloseOff"/>
+<clip nameId="logo" layer1="Logo" x="0" y="5"/>
+<clip nameId="timeLine" x="2" y="2" round="false" width="756" height="5" color1="0xD6D6D6" color2="0x898989" color3="0x005ea8"/>
+<clip nameId="btSearch" x="507" y="12" width="103" height="32" stateOn="BtSearchOn" stateOff="BtSearchOff">
+<label>
+<![CDATA[<b>Recherche</b>]]>
+</label>
+</clip>
+<clip nameId="searchEngine">
+<label>
+<![CDATA[/recherche/?q=|&x=73&y=14&rub=0]]>
+</label>
+</clip>
+<clip nameId="btCopy" x="360" width="73" height="30" stateOn="BtCopyOn" stateOff="BtCopyOff">
+
+<label>
+<![CDATA[<b>Copier</b>]]>
+</label>
+</clip>
+<clip nameId="mcShare">
+<label>
+<![CDATA[Permalien :]]>
+</label>
+</clip>
+<clip nameId="mcBlog">
+<label>
+<![CDATA[Lecteur exportable :|allocine_blog]]>
+</label>
+</clip>
+<clip nameId="btScrollRight" stateOn="BtScrollRightOn" stateOff="BtScrollRightOff"/>
+<clip nameId="btScrollLeft" stateOn="BtScrollLeftOn" stateOff="BtScrollLeftOff"/>
+<clip nameId="btReplay" type="navCenter" action="replayHandler" y="115" stateOn="BtReplayOn" stateOff="BtReplayOff">
+
+<label>
+<![CDATA[<b>REJOUER</b>]]>
+</label>
+</clip>
+<clip nameId="btShare" type="navCenter" action="shareHandler" x="110" y="115" stateOn="BtShareOn" stateOff="BtShareOff">
+<label>
+<![CDATA[<b>PARTAGER</b>]]>
+</label>
+</clip>
+<clip nameId="btBlog" type="navCenter" action="blogHandler" x="220 " y="115" stateOn="BtBlogOn" stateOff="BtBlogOff">
+<label>
+<![CDATA[<b>SUR MON BLOG</b>]]>
+</label>
+</clip>
+<clip nameId="btVideoSim" type="navCenter" action="videoSimHandler" x="330" y="115" stateOn="BtVideoSimOn" stateOff="BtVideoSimOff">
+<label>
+<![CDATA[<b>VIDÉOS <br>SIMILAIRES</b>]]>
+
+</label>
+</clip>
+<clip nameId="titleEndScreen" >
+<label>
+<![CDATA[<b>Choisissez votre prochaine vidéo </b>]]>
+</label>
+</clip>
+<clip nameId="textError" >
+<label>
+<![CDATA[<b>Erreur : cette vidéo n'est pas disponible.</b>]]>
+</label>
+</clip>
+<clip nameId="errorAdBlock" >
+<label>
+<![CDATA[Cette vidéo ne se lance pas ?<br>Vous avez probablement activé une extension comme AdBlock dans votre navigateur. Merci de la désactiver et de recharger la page afin de lire cette vidéo.<br>Si votre problème persiste, n’hésitez pas à <b><a href='http://allocine.uservoice.com/forums/30575-allocin-v6-probl-mes-et-erreurs'>nous contacter</a></b> !]]>
+</label>
+</clip>
+
+</AcVisionSkin>
+<services>
+<!--service className="McXitiService" nameService="xiti" file="services/xitiService.swf" pathConfig="http://www.allocine.fr/Modules/video/xml/XiTiTag.xml" /-->
+<service className="McVastService" nameService="vast" file="services/vastService.swf" pathConfig="http://smart.allocine.fr/call/pubx/10936/[part]/7292/S/[timestamp]/[target]" >
+<![CDATA[<b>Publicité</b> : votre vidéo commence dans | secondes]]>
+</service>
+<!---->
+</services>
+</AcVision>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/allocine_test/exemple.json	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,735 @@
+{
+
+    "views": [
+        {
+            "id": "0",
+            "contents": [
+                "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290"
+            ],
+            "annotation_types": [
+                "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+                "c_ab04d230-4dc2-11e1-975a-c8bcc896c290"
+            ]
+        },
+        {
+            "meta": {
+                "stat": "1,1,1,1,1,1,1,2,1,1,3,3,3,3,3,2,3,3,2,2,3,3,4,3,3,2,2,2,2,5,5,6,9,9,8,7,6,6,6,3,3,3,3,3,4,3,3,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1"
+            },
+            "id": "stat",
+            "contents": [
+                "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290"
+            ]
+        }
+    ],
+    "tags": null,
+    "lists": [
+        {
+            "items": [
+                {
+                    "id-ref": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290"
+                },
+                {
+                    "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290"
+                }
+            ],
+            "meta": {
+                "dc:contributor": "undefined",
+                "dc:created": "2012-02-21T10:49:02.323681",
+                "dc:creator": "undefined",
+                "id-ref": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+                "dc:title": "Découpages personnels",
+                "editable": "false",
+                "dc:modified": "2012-02-21T10:49:02.323681",
+                "dc:description": ""
+            },
+            "id": "g_ab04ca73-4dc2-11e1-985a-c8bcc896c290"
+        }
+    ],
+    "medias": [
+        {
+            "origin": "0",
+            "http://advene.liris.cnrs.fr/ns/frame_of_reference/ms": "o=0",
+            "href": "rtmp://localhost/vod/pj_harvey_-_let_england_shake.mp4",
+            "meta": {
+                "dc:contributor": "IRI",
+                "item": {
+                    "name": "streamer",
+                    "value": "rtmp://localhost/vod/"
+                },
+                "dc:created": "2012-02-02T18:23:47.515370",
+                "dc:duration": 252560,
+                "dc:creator": "IRI",
+                "dc:created.contents": "2012-02-06",
+                "dc:title": "pj harvey - let england shake",
+                "dc:creator.contents": "IRI",
+                "dc:modified": "2012-02-06T18:03:11.237247",
+                "dc:description": ""
+            },
+            "id": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "unit": "ms"
+        }
+    ],
+    "meta": {
+        "dc:contributor": "admin",
+        "dc:created": "2012-02-02T18:23:48.096335",
+        "dc:creator": "admin",
+        "main_media": {
+            "id-ref": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290"
+        },
+        "dc:description": "",
+        "dc:title": "front project : pj harvey - let england shake",
+        "id": "aab75563-4dc2-11e1-8cf2-c8bcc896c290",
+        "dc:modified": "2012-02-21T11:48:56.825124"
+    },
+    "annotations": [
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "",
+                "color": "16763904",
+                "polemics": [ ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 1296,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.323709",
+                "dc:modified": "2012-02-21T10:49:02.323709",
+                "dc:creator": "perso"
+            },
+            "end": 29502,
+            "tags": null,
+            "color": "16763904",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_039EC88E-10CB-8864-38CF-9F86A14DD43F"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "",
+                "color": "26316",
+                "polemics": [ ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 30327,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.323709",
+                "dc:modified": "2012-02-21T10:49:02.323709",
+                "dc:creator": "perso"
+            },
+            "end": 87536,
+            "tags": null,
+            "color": "26316",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_67066BA8-E20F-15D7-523A-9F86AFD3A3B0"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "",
+                "color": "10027212",
+                "polemics": [ ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 88036,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.323709",
+                "dc:modified": "2012-02-21T10:49:02.323709",
+                "dc:creator": "perso"
+            },
+            "end": 152703,
+            "tags": null,
+            "color": "10027212",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_6E2F938C-F6C8-67A6-3FE2-9F86B7C5A0CA"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "",
+                "color": "16737792",
+                "polemics": [ ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 153203,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.323709",
+                "dc:modified": "2012-02-21T10:49:02.323709",
+                "dc:creator": "perso"
+            },
+            "end": 174749,
+            "tags": null,
+            "color": "16737792",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_1F2CCE4C-B192-3319-FF60-9F86C0E6003F"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "",
+                "color": "16711680",
+                "polemics": [ ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 175249,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.323709",
+                "dc:modified": "2012-02-21T10:49:02.323709",
+                "dc:creator": "perso"
+            },
+            "end": 250938,
+            "tags": null,
+            "color": "16711680",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_CEECA32C-4033-6A4D-4E64-9F86CD41D5CE"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "globalement --",
+                "color": "2194379",
+                "polemics": [
+                    "KO"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 39491,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 68421,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_a0774ce1-4e5a-11e1-bb2b-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "ok ??",
+                "color": "2194379",
+                "polemics": [
+                    "Q"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 43165,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 56941,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_6bd10b6e-50bb-11e1-951f-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "dude ??",
+                "color": "2194379",
+                "polemics": [
+                    "Q"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 63829,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 212610,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_a40ea52b-50bb-11e1-8226-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "TC ??",
+                "color": "2194379",
+                "polemics": [
+                    "Q"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 82197,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 95514,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_7d450700-50a8-11e1-a79d-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "kar --",
+                "color": "2194379",
+                "polemics": [
+                    "KO"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 114800,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 134546,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_61f0a951-4e5b-11e1-a15a-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "coucou --",
+                "color": "2194379",
+                "polemics": [
+                    "KO"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 116178,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 133168,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_876e13c2-4e5b-11e1-896e-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "alors --",
+                "color": "2194379",
+                "polemics": [
+                    "KO"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 117096,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 140056,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_f70a1e40-4e5a-11e1-8466-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "Dude --",
+                "color": "2194379",
+                "polemics": [
+                    "KO"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 123525,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 149699,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_d355705c-50a4-11e1-af3c-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "alors ??",
+                "color": "2194379",
+                "polemics": [
+                    "Q"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 126280,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 151536,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_cd1158a1-50bb-11e1-a4c6-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "Dude, ça va ==",
+                "color": "2194379",
+                "polemics": [
+                    "REF"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 126280,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 151536,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_72ff6e66-50e4-11e1-a36b-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "Dude --",
+                "color": "2194379",
+                "polemics": [
+                    "KO"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 126280,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 151536,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_0f59c7de-50bb-11e1-9e35-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "contrib",
+                "color": "2194379",
+                "polemics": [ ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 157506,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 182762,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_dbadbd40-4e5b-11e1-b9b3-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "globalement ++",
+                "color": "2194379",
+                "polemics": [
+                    "OK"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 694540,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 833448,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_b2b3cfb5-4e5a-11e1-8adb-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "Dude ++",
+                "color": "2194379",
+                "polemics": [
+                    "OK"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 694540,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 833448,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_b7e5c2d4-50a4-11e1-9f06-c8bcc896c290"
+        },
+        {
+            "content": {
+                "mimetype": "application/x-ldt-structured",
+                "description": "",
+                "img": {
+                    "src": ""
+                },
+                "title": "TC bon. ++",
+                "color": "2194379",
+                "polemics": [
+                    "OK"
+                ],
+                "audio": {
+                    "mimetype": "audio/mp3",
+                    "src": "",
+                    "href": null
+                }
+            },
+            "begin": 694540,
+            "meta": {
+                "dc:contributor": "perso",
+                "id-ref": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+                "dc:created": "2012-02-21T10:49:02.326350",
+                "dc:modified": "2012-02-21T10:49:02.326350",
+                "dc:creator": "perso"
+            },
+            "end": 833448,
+            "tags": null,
+            "color": "2194379",
+            "media": "9e6ccd47-4dc2-11e1-b6c2-c8bcc896c290",
+            "id": "s_85dd0bc5-50a8-11e1-a6dc-c8bcc896c290"
+        }
+    ],
+    "annotation-types": [
+        {
+            "dc:contributor": "perso",
+            "dc:creator": "perso",
+            "dc:title": "chapitrage",
+            "id": "c_ab04d051-4dc2-11e1-b6f3-c8bcc896c290",
+            "dc:created": "2012-02-21T10:49:02.323709",
+            "dc:description": "",
+            "dc:modified": "2012-02-21T10:49:02.323709"
+        },
+        {
+            "dc:contributor": "perso",
+            "dc:creator": "perso",
+            "dc:title": "contributions",
+            "id": "c_ab04d230-4dc2-11e1-975a-c8bcc896c290",
+            "dc:created": "2012-02-21T10:49:02.326350",
+            "dc:description": "",
+            "dc:modified": "2012-02-21T10:49:02.326350"
+        }
+    ]
+
+}
\ No newline at end of file
Binary file test/integration/allocine_dossier_independant/allocine_test/skin_fr.swf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/css/LdtPlayer.css	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,902 @@
+#demo-frame > div.demo { padding: 5px !important; };
+
+button.ui-button-icon-only  {
+  height:1.5em;
+  width:1.5em;
+}
+
+#Ldt-loader {
+  background:url(imgs/loader.gif) no-repeat;
+  width:20px;
+  height:16px;
+  float:left;
+}
+
+/* general class for all buttons */
+.Ldt-button {
+
+}
+
+.Ldt-SegmentsWidget {
+ /* overflow: auto; /* clear the floats */
+  margin-top: 1px;
+  padding-bottom: 8px; /* FIXME: only a temporary fix. This should be put into the layout manager. */
+}
+
+.Ldt-iri-chapter {
+  position: absolute;
+  height: 10px;
+  border-right: 1px solid white;
+}
+
+.Ldt-SegmentPositionMarker {
+  position: absolute;
+  z-index: 100;
+  width: 1px;
+  height: 10px;
+  background-color: white;
+}
+.tooltip {
+  display:none;
+  background:transparent url(imgs/white_arrow_mini.png);
+  font-size:12px;
+  height:55px;
+  width:180px;
+  padding:10px;
+  padding-left:15px;
+  padding-top:15px;
+  padding-right:15px;
+  color:#000;
+  font-family: "Trebuchet MS", "Helvetica", "Arial",  "Verdana", "sans-serif";
+}
+#Ldt-Root{
+  font-family: "Trebuchet MS", "Helvetica", "Arial",  "Verdana", "sans-serif";
+}
+#Ldt-Hat{
+  height:3px;
+}
+
+.Ldt-AnnotationsWidget {
+  font-size: 12px;
+  font-family: "Arial",  "Verdana", "sans-serif";
+  background-color:#eeeeee;
+  background:url('imgs/wire_pattern.png') repeat scroll transparent ;
+  border: 1px solid #b6b8b8;
+}
+
+.Ldt-Annotation-DoubleBorder {
+  border: 1px solid white;
+  overflow: auto;
+}
+
+.Ldt-AnnotationContent {
+  padding:5px;
+  padding-left: 12px;
+
+}
+
+.Ldt-fbShare {
+  display: block;
+  float: left;
+  width: 24px;
+  height: 24px;
+  background:url('imgs/facebook.png');
+}
+
+.Ldt-TwShare {
+  display: block;
+  float: left;
+  width: 24px;
+  height: 24px;
+  background:url('imgs/twitter.png');
+}
+
+.Ldt-GplusShare {
+  display: block;
+  float: left;
+  width: 24px;
+  height: 24px;
+  background:url('imgs/google.png');
+}
+
+.Ldt-SaTitle{
+  padding-top:2px;
+  padding-bottom:3px;
+  font-size: 12pt;
+  color : #0068c4;
+}
+
+.Ldt-SaDescription{
+  font-size:12px;
+}
+
+.Ldt-SaKeywords {  
+  font-weight:bold;  
+  font-size:10px;
+}
+
+.Ldt-AnnotationShareIcons {
+  float:right;
+  position: relative;
+}
+
+
+#Ldt-PlaceHolder{
+  position:absolue;
+  float:none;
+}
+
+.Ldt-Segments{
+  float:left;
+  font-size: 62.5%;
+}
+
+.Ldt-mode-radio{
+  visibility:hidden;
+  height:0px;
+  display:none
+}
+
+/* player */
+.Ldt-controler {
+  font-size: 62.5%;
+  font-family: "Trebuchet MS", "Helvetica", "Arial",  "Verdana", "sans-serif";
+  background:url('imgs/player_gradient.png') repeat-x transparent ;
+  height: 25px;
+  border-top: 1px solid #b6b8b8;
+  border-bottom: 1px solid #b6b8b8;
+}
+
+
+.Ldt-LeftPlayerControls {
+  float:left;
+}
+
+.Ldt-RightPlayerControls {
+  float: right;
+}
+
+.Ldt-button {
+  border-left: 1px solid #b6b8b8;
+  float: left;
+  cursor: pointer;
+ 
+}
+
+.Ldt-CtrlPlay {
+
+  background-position: 0 0;
+  width: 59px;
+  height: 25px;
+}
+
+.Ldt-CtrlPlay-PlayState {
+  background:url('imgs/play_sprite.png') no-repeat transparent ;
+}
+
+.Ldt-CtrlPlay-PauseState {
+  background:url('imgs/pause_sprite.png') no-repeat transparent ;
+}
+
+.Ldt-CtrlPlay:hover {
+  background-position: 0 -25px;
+}
+
+.Ldt-CtrlPlay:active {
+  background-position: 0 -50px;
+}
+
+.Ldt-CtrlAnnotate {
+  background:url('imgs/annotate_sprite.png') no-repeat scroll 0 0 transparent ;
+  width: 33px;
+  height: 25px;
+  border-right: 1px solid #b6b8b8;
+  float: left;
+}
+
+.Ldt-CtrlAnnotate:hover {
+  background-position: 0 -25px;
+}
+
+.Ldt-CtrlAnnotate:active {
+  background-position: 0 -50px;
+}
+
+.Ldt-CtrlSearch {
+  background:url('imgs/search_sprite.png') no-repeat scroll 0 0 transparent ;
+  width: 33px;
+  height: 25px;
+  border-right: 1px solid #b6b8b8;
+  float: left;
+  border-left: none;
+}
+
+.Ldt-CtrlSearch:hover {
+  background-position: 0 -25px;
+}
+
+.Ldt-CtrlSearch:active {
+  background-position: 0 -50px;
+}
+
+.LdtSearch {
+  display: none;
+  width: 165px;
+  height: 25px;
+  border: 1px;
+  border-color: #CFCFCF;
+  float: left;
+  text-align: center;
+}
+
+.Ldt-Time {
+  position: inherit;
+  float: left;
+  border-right: 1px solid #b6b8b8;
+  height: 25px;
+  padding-right: 2px;
+  font-size: 12px;
+  font-family: Arial, Verdana, sans-serif;
+}
+
+.Ldt-ElapsedTime {
+  margin-top: 4px;
+  margin-right: 2px;
+  float: left;
+  color: #4a4a4a;
+}
+
+.Ldt-TimeSeparator {
+  margin-top: 4px;      
+  float: left;
+  padding-left: 1px;
+  padding-right: 1px;
+}
+
+.Ldt-TotalTime {
+  margin-top: 4px;
+  margin-left: 2px;
+  float: left;
+  color: #b2b2b2; 
+}
+
+.Ldt-CtrlSound {
+  width: 33px;
+  height: 25px;
+  border-right: 1px solid #b6b8b8;
+  float: right;
+  border-left: none;
+}
+
+.Ldt-CtrlSound-SoundState {
+  background:url('imgs/sound_sprite.png') no-repeat scroll 0 0 transparent ;
+}
+
+.Ldt-CtrlSound-MuteState {
+  background:url('imgs/mute_sprite.png') no-repeat scroll 0 0 transparent ;
+}
+
+.Ldt-CtrlSound:hover {
+  background-position: 0 -25px;
+}
+
+.Ldt-CtrlSound:active {
+  background-position: 0 -50px;
+}
+/*
+.Ldt-CtrlSound {
+  float: right;
+  border-left: none;
+  height: 25px;
+  top: 7px;
+  position: inherit;
+}
+*/    
+.Ldt-cleaner {
+  clear:both;
+}
+
+/* Arrow Widget */
+.Ldt-arrowWidget {
+  position: relative;
+
+  height:16px;
+  width:27px;
+  margin-bottom: -3px;
+  z-index: 4;
+  left: 0%;
+}
+
+.Ldt-arrowLeftEdge {
+  background:url('imgs/left_edge_arrow.png') no-repeat scroll 0 0 transparent ;
+}
+
+.Ldt-arrowCenter {
+  background:url('imgs/arrow.png') no-repeat scroll 0 0 transparent ;
+}
+
+.Ldt-arrowRightEdge {
+  background:url('imgs/right_edge_arrow.png') no-repeat scroll 0 0 transparent ;
+}
+
+.cleaner {
+  clear:both;
+}
+
+.share {
+  background:url('imgs/widget20.png') no-repeat scroll 0 0 transparent ;
+  display:block;
+  height:16px;
+  line-height:16px !important;
+  overflow:hidden;
+  width:16px;
+  float:left;
+  cursor:pointer;
+  margin:2px;
+}
+.shareFacebook{
+  background-position:0 -704px;
+}
+.shareMySpace{
+  background-position:0 -736px;
+}
+.shareTwitter{
+  background-position:0 -1072px;
+}
+.shareGoogle{
+  background-position:0 -752px;
+}
+.shareDelicious{
+  background-position:0 -672px;
+}
+.shareJamesPot{
+  background-position:0 -1808px;
+}
+
+.tip{
+  position: absolute;
+  padding : 3px;
+  z-index: 10000000000;
+  max-width: 200px;
+  background: transparent url("imgs/white_arrow_long.png");
+  font-size: 12px;
+  height: 125px;
+  width: 180px;
+  padding: 10px;
+  padding-left: 15px;
+  padding-top: 15px;
+  padding-right: 15px;
+  color: black;
+  font-family: "Trebuchet MS", "Helvetica", "Arial", "Verdana", "sans-serif";
+  overflow:hidden;
+}
+
+/* slider */
+.Ldt-SliderMinimized {
+  height: 6px;
+}
+
+.Ldt-SliderMaximized {
+  height: 11px;
+}
+
+.Ldt-sliderElementMinimized {
+  width: 100%;
+  height: 5px;
+}
+
+.Ldt-sliderElementMaximized {
+  width: 100%;
+  height: 10px;
+}
+
+.Ldt-sliderBackground  {
+  background-color: #B6B8B8;
+  position: absolute;
+  z-index: 2;
+  bottom: 1px;
+  width: 100%;
+  height: 5px;
+
+}
+
+.Ldt-sliderForeground  {
+  background-color: #747474;
+  z-index: 2;
+  width: 0px;
+  position: absolute;
+  bottom: 1px;
+  height: 5px;
+}
+
+.Ldt-sliderPositionMarker {
+  position: absolute;
+  z-index: 100;
+  background-color: #f7268e;
+  height: 5px;
+  width: 5px;
+  bottom: 1px;
+  border-left: 1px solid white;
+  border-right: 1px solid white;
+}
+
+/* tweet Widget */
+.Ldt-tweetWidget {
+  font-size: 12px;
+  font-family: "Arial",  "Verdana", "sans-serif";
+  background:url('imgs/wire_pattern.png') repeat scroll transparent ;
+  border: 1px solid #b6b8b8;
+  border-top: none;
+  overflow: auto;
+}
+
+.Ldt-tweet-DoubleBorder {
+  border: 1px solid white;
+  padding: 5px;
+  overflow: auto;
+}
+
+.Ldt-tweetAvatar {
+  float: left;
+}
+
+.Ldt-tweetAvatar-profileArrow {
+  float: left;
+  height: 48px;
+  margin-left: 5px;
+  margin-right: 5px;
+  background:url('imgs/profile_arrow.png');
+  background-position: 7 10px;
+}
+
+.Ldt-tweet_userHandle {
+   float: left;
+   color: #5c8df1;
+}
+
+.Ldt-tweet_realName {
+  float: left;
+  margin-left: 3px;
+}
+
+.Ldt-tweetContents {
+}
+
+.Ldt-tweet_date {
+  float: left;
+}
+
+.Ldt-tweetWidgetKeepOpen {
+  position: relative;
+  float: right;
+  height: 17px;
+  width: 17px;
+  margin-right: 1px;
+  background:url('imgs/minimize.png');
+}
+
+.Ldt-tweetWidgetMinimize {
+  position: relative;
+  float: right;
+  height: 17px;
+  width: 17px;
+  right: 9px;
+  background:url('imgs/minimize.png');
+}
+
+.Ldt-tweetWidget * a:link {
+  color: #729efa;
+
+}
+
+.Ldt-TweetReply {
+  float: left;
+  margin-left: 16px;
+}
+
+.Ldt-TweetReplyIcon {
+  background:url('imgs/reply_sprite.png') no-repeat scroll 0 0 transparent ;
+  width: 14px;
+  height: 11px;
+  float: left;
+  margin-top: 2px;
+}
+
+.Ldt-TweetReplyIcon:hover {
+  background-position: 0 -11px;
+}
+
+.Ldt-TweetReplyIcon:active {
+  background-position: 0 -22px;
+}
+
+.Ldt-Retweet {
+  float: left;
+  margin-left: 16px;
+}
+
+.Ldt-RetweetIcon {
+  background:url('imgs/retweet_sprite.png') no-repeat scroll 0 0 transparent ;
+  width: 14px;
+  height: 8px;
+  float: left;
+  margin-top: 3px;
+}
+
+.Ldt-RetweetIcon:hover {
+  background-position: 0 -8px;
+}
+
+.Ldt-RetweetIcon:active {
+  background-position: 0 -16px;
+}
+
+/* styling of a "++" in a tweet */
+.Ldt-PolemicPlusPlus {
+  background-color: #1d973d;
+}
+
+/* styling of a "==" in a tweet */
+.Ldt-PolemicEqualEqual {
+  background-color: #5c8df1
+}
+
+/* styling of a "--" in a tweet */
+.Ldt-PolemicMinusMinus {
+  background-color: #ce0a15;
+}
+
+/* styling of a "??" in a tweet */
+.Ldt-PolemicQuestion {
+  background-color: #c5a62d;
+}
+
+/* the styling of a spacer div */
+.Ldt-spacer {
+  background-color:#eeeeee;
+}
+
+/* sparkline widget */
+.Ldt-sparklineWidget {
+  position: relative;
+  margin-bottom: 5px;
+}
+
+.Ldt-sparkLinePositionMarker {
+  position: absolute;
+  top: 0px;
+  width: 0px;
+  background-color: #333333;
+  border-right: solid 1px pink;
+  z-index: 3;
+  opacity: 0.2;
+}
+
+.Ldt-sparkLine {
+  position: absolute;
+  top: 0px;
+}
+
+.Ldt-sparkLineClickOverlay {
+  position: absolute;
+  width: 640px;
+  height: 60px;
+  z-index: 4;
+  top: 0px;
+  opacity: 0.3;
+}
+
+.Ldt-sliceWidget {
+  position: relative;
+  width: 100%;
+  height: 25px;
+  margin-top: 3px;
+}
+
+.Ldt-sliceBackground {
+  width: 100%;
+  background-color: #b6b8b8;
+  height: 12px;
+}
+
+.Ldt-sliceZone {
+  position: absolute;
+  top: 0px;
+  background:url('imgs/wire_pattern.png') repeat scroll transparent;
+  height: 12px;
+  z-index: 2;
+}
+
+.Ldt-sliceLeftHandle {
+  position: absolute;
+  top: 0px;
+  height: 25px;
+  width: 7px;
+  background:url('imgs/left_handle.gif') no-repeat scroll transparent;
+  z-index: 2;
+}
+
+.Ldt-sliceRightHandle {
+  position: absolute;
+  top: 0px;
+  height: 25px;
+  width: 7px;
+  background:url('imgs/right_handle.gif') no-repeat scroll transparent;
+  z-index: 2;
+}
+
+.Ldt-createAnnotationWidget {
+  font-size: 12px;
+  font-family: "Arial",  "Verdana", "sans-serif";
+  background-color:#eeeeee;
+  background:url('imgs/wire_pattern.png') repeat scroll transparent ;
+  border: 1px solid #b6b8b8;  
+}
+
+.Ldt-createAnnotation-DoubleBorder {
+  border: 1px solid white;
+  overflow: auto;
+  padding: 7px;
+}
+
+.Ldt-createAnnotation-Title {
+  font-size: 12pt;
+  color : #0068c4;  
+  float: left;
+  margin-right: 5px;
+}
+
+.Ldt-createAnnotation-TimeFrame {
+  font-size: 12pt;
+  color : #ff5589;  
+  float: left;
+}
+
+.Ldt-createAnnotation-Container {  
+  display: table;
+  border-collapse: collapse;
+  width: 100%;
+}
+
+.Ldt-createAnnotation-userAvatar {  
+  width: 48px;
+  display: table-cell;
+  vertical-align: top;
+}
+
+.Ldt-createAnnotation-userAvatar img {  
+  display: block;
+  border: 1px solid #babcbc;
+}
+
+.Ldt-createAnnotation-profileArrow {
+  display: table-cell;
+  vertical-align: top;
+  height: 48px;
+  width: 15px;
+  padding-right: 5px;
+  padding-left: 3px;
+  background:url('imgs/profile_arrow.png');
+  background-repeat: no-repeat;
+  background-position: center;
+}
+
+.Ldt-createAnnotation-Description {
+  display: table-cell;   
+  width: 100%;
+  height: 48px;  
+  vertical-align: top;
+  -webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
+  -moz-box-sizing: border-box;    /* Firefox, other Gecko */
+  box-sizing: border-box;         /* Opera/IE 8+ */
+}
+
+.Ldt-createAnnotation-present-keyword {
+  border: 1px solid #ffffff;
+  background-color: #217bcb;
+  color: #ffffff;
+  padding: 3px;
+  padding-left: 4px;
+  padding-right: 4px;
+  cursor: pointer;
+}
+
+.Ldt-createAnnotation-absent-keyword {
+  border: 1px solid #ffffff;
+  background-color: #d93c71;
+  color: #ffffff;
+  padding: 3px;
+  padding-left: 4px;
+  padding-right: 4px;
+  cursor: pointer;
+}
+
+.Ldt-createAnnotation-submitButton {
+  float: right;
+  background-color: #d93c71;
+  color: #ffffff;
+  cursor: pointer;
+  background-image: url('imgs/submit_annotation.png');
+  background-repeat: no-repeat;
+  height: 48px;
+  width: 48px;
+  position: relative;
+  margin-top: 3px;
+}
+
+.Ldt-createAnnotation-endScreen {
+  background-color: #ffffff;
+  margin-left: 5px;
+  margin-right: 5px;
+  border: 1px solid #d6d6d6;
+  padding: 10px;
+  font-size: 13px;
+  font-weight: bold;
+  color : #f7268e;
+  text-align: center;
+}
+
+.Ldt-createAnnotation-errorMessage {
+  color: #D93C71;
+}
+
+.Ldt-createAnnotation-Minimize {
+  position: relative;
+  float: right;
+  height: 17px;
+  width: 17px;
+  right: 0px;
+  background:url('imgs/minimize.png');
+}
+
+/* polemic tweet annotation buttons */
+.Ldt-createAnnotation-polemics {
+  padding-top: 2px;
+}
+
+.Ldt-createAnnotation-polemic-button {
+  margin: 0;
+  border: 0;
+  padding: 0;
+  margin-right: 2px;
+  width: 38px;
+  height: 26px;
+  text-indent: -999px;
+  background: url(imgs/polemic_buttons_sprite.png);
+}
+
+.Ldt-createAnnotation-polemic-positive:hover {
+    background-position: 0 -26px;
+}
+.Ldt-createAnnotation-polemic-positive.Ldt-createAnnotation-polemic-active {
+    background-position: 0 -52px;
+}
+
+.Ldt-createAnnotation-polemic-negative {
+    background-position: -38px 0;
+}
+.Ldt-createAnnotation-polemic-negative:hover {
+    background-position: -38px -26px;
+}
+.Ldt-createAnnotation-polemic-negative.Ldt-createAnnotation-polemic-active {
+    background-position: -38px -52px;
+}
+
+.Ldt-createAnnotation-polemic-reference {
+    background-position: -76px 0;
+}
+.Ldt-createAnnotation-polemic-reference:hover {
+    background-position: -76px -26px;
+}
+.Ldt-createAnnotation-polemic-reference.Ldt-createAnnotation-polemic-active {
+    background-position: -76px -52px;
+}
+
+.Ldt-createAnnotation-polemic-question {
+    background-position: -114px 0;
+}
+.Ldt-createAnnotation-polemic-question:hover {
+    background-position: -114px -26px;
+}
+.Ldt-createAnnotation-polemic-question.Ldt-createAnnotation-polemic-active {
+    background-position: -114px -52px;
+}
+
+.Ldt-createAnnotation-spinner {
+	display: inline;
+	width: 16px;
+	height: 16px;
+	background-image('imgs/spinner.gif');
+}
+
+.Ldt-createAnnotation-endScreen-TweetLink {
+	width: 54px;
+	height: 20px;
+	background-image('imgs/tweet_button.png');
+}
+
+.Ldt-createAnnotation-endScreen-FbLink {
+	width: 55px;
+	height: 20px;
+	background-image('imgs/facebook_button.png');
+}
+
+.Ldt-createAnnotation-endScreen-GplusLink {
+	width: 50px;
+	height: 20px;
+	background-image('imgs/gplus_button.png');
+}
+/* AnnotationsListWidget */
+
+.Ldt-AnnotationsListWidget {
+  font-size: 12px;
+  font-family: "Arial",  "Verdana", "sans-serif";
+  border: 1px solid #b6b8b8;
+  overflow: auto;
+  max-height: 480px;
+}
+
+.Ldt-AnnotationsListWidget ul {
+  padding: 5px;
+}
+
+.Ldt-AnnotationsListWidget li {
+  list-style-type: none;
+  cursor: pointer;
+  display: table-row;
+  padding-top: 2px;
+  height: 64px;
+}
+
+.Ldt-AnnotationsListWidget li:hover {
+  background-color: #e9e9e9;
+}
+
+.Ldt-AnnotationsListWidget li a {
+  text-decoration: none;
+}
+
+.Ldt-AnnotationsList-Caption {
+  float: left;
+  display: table-cell;
+  vertical-align: middle;
+  height: 64px;
+  width: 64px;
+  background-image: url('imgs/video_sequence.png');
+}
+
+.Ldt-AnnotationsList-Duration {  
+  color : #f7268e;  
+  float: right;
+  text-align: left;
+  width: 120px;
+}
+
+.Ldt-AnnotationsList-Title {
+  color: #0068c4;
+  font-size: 13px;
+  display: table-cell;
+  width: 80%;
+}
+
+/* Tagcloud */
+
+.Ldt-TagCloud {
+    font-family: "PT Sans", Arial, Helvetica, sans-serif;
+}
+
+.Ldt-TagCloud ul {
+    list-style: none; padding: 0; margin: 5px; text-align: center;
+}
+
+.Ldt-TagCloud li {
+    display: inline-block; margin: 2px; cursor:pointer; cursor:hand;
+}
+
+.Ldt-TagCloud li:hover {
+    color: #0099ff;
+}
+
+.Ldt-TagCloud-actif {
+    color: #c000c0;
+}
Binary file test/integration/allocine_dossier_independant/css/imgs/annotate_arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/annotate_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/black.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/black_arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/black_arrow_big.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/black_big.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/delicious.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/facebook.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/facebook_button.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/google.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/gplus_button.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/grey_arrow_Show.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/left_edge_arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/left_handle.gif has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/linkedin.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/loader.gif has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/loader_fc.gif has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/loader_fc2.gif has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/minimize.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/mute_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/pause_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/play_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/player_gradient.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/polemic_buttons_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/profile_arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/purple_arrow_Show.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/reply_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/retweet_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/right_edge_arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/right_handle.gif has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/search_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/sound_sprite.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/spinner.gif has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/submit_annotation.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/transBlack.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/tweet_button.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/twitter.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/user_default_icon.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/video_sequence.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/white.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/white_arrow.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/white_arrow_big.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/white_arrow_long.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/white_arrow_mini.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/white_big.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/widget20.png has changed
Binary file test/integration/allocine_dossier_independant/css/imgs/wire_pattern.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/LdtPlayer-release.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,5833 @@
+/* 
+ * 	
+ *	Copyright 2010-2012 Institut de recherche et d'innovation 
+ *	contributor(s) : Karim Hamidou, Samuel Huron 
+ *	 
+ *	contact@iri.centrepompidou.fr
+ *	http://www.iri.centrepompidou.fr 
+ *	 
+ *	This software is a computer program whose purpose is to show and add annotations on a video .
+ *	This software is governed by the CeCILL-C license under French law and
+ *	abiding by the rules of distribution of free software. You can  use, 
+ *	modify and/ or redistribute the software under the terms of the CeCILL-C
+ *	license as circulated by CEA, CNRS and INRIA at the following URL
+ *	"http://www.cecill.info". 
+ *	
+ *	The fact that you are presently reading this means that you have had
+ *	knowledge of the CeCILL-C license and that you accept its terms.
+*/
+/*! LAB.js (LABjs :: Loading And Blocking JavaScript)
+    v2.0.3 (c) Kyle Simpson
+    MIT License
+*/
+
+(function(global){
+	var _$LAB = global.$LAB,
+	
+		// constants for the valid keys of the options object
+		_UseLocalXHR = "UseLocalXHR",
+		_AlwaysPreserveOrder = "AlwaysPreserveOrder",
+		_AllowDuplicates = "AllowDuplicates",
+		_CacheBust = "CacheBust",
+		/*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/
+		_BasePath = "BasePath",
+		
+		// stateless variables used across all $LAB instances
+		root_page = /^[^?#]*\//.exec(location.href)[0],
+		root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0],
+		append_to = document.head || document.getElementsByTagName("head"),
+		
+		// inferences... ick, but still necessary
+		opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style),
+
+/*!START_DEBUG*/
+		// console.log() and console.error() wrappers
+		log_msg = function(){}, 
+		log_error = log_msg,
+/*!END_DEBUG*/
+		
+		// feature sniffs (yay!)
+		test_script_elem = document.createElement("script"),
+		explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29
+		real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append?
+		script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
+		
+		// XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers)
+		xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko
+	;
+
+/*!START_DEBUG*/
+	// define console wrapper functions if applicable
+	if (global.console && global.console.log) {
+		if (!global.console.error) global.console.error = global.console.log;
+		log_msg = function(msg) { global.console.log(msg); };
+		log_error = function(msg,err) { global.console.error(msg,err); };
+	}
+/*!END_DEBUG*/
+
+	// test for function
+	function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; }
+
+	// test for array
+	function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; }
+
+	// make script URL absolute/canonical
+	function canonical_uri(src,base_path) {
+		var absolute_regex = /^\w+\:\/\//;
+		
+		// is `src` is protocol-relative (begins with // or ///), prepend protocol
+		if (/^\/\/\/?/.test(src)) {
+			src = location.protocol + src;
+		}
+		// is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /)
+		else if (!absolute_regex.test(src) && src.charAt(0) != "/") {
+			// prepend `base_path`, if any
+			src = (base_path || "") + src;
+		}
+		// make sure to return `src` as absolute
+		return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src);
+	}
+
+	// merge `source` into `target`
+	function merge_objs(source,target) {
+		for (var k in source) { if (source.hasOwnProperty(k)) {
+			target[k] = source[k]; // TODO: does this need to be recursive for our purposes?
+		}}
+		return target;
+	}
+
+	// does the chain group have any ready-to-execute scripts?
+	function check_chain_group_scripts_ready(chain_group) {
+		var any_scripts_ready = false;
+		for (var i=0; i<chain_group.scripts.length; i++) {
+			if (chain_group.scripts[i].ready && chain_group.scripts[i].exec_trigger) {
+				any_scripts_ready = true;
+				chain_group.scripts[i].exec_trigger();
+				chain_group.scripts[i].exec_trigger = null;
+			}
+		}
+		return any_scripts_ready;
+	}
+
+	// creates a script load listener
+	function create_script_load_listener(elem,registry_item,flag,onload) {
+		elem.onload = elem.onreadystatechange = function() {
+			if ((elem.readyState && elem.readyState != "complete" && elem.readyState != "loaded") || registry_item[flag]) return;
+			elem.onload = elem.onreadystatechange = null;
+			onload();
+		};
+	}
+
+	// script executed handler
+	function script_executed(registry_item) {
+		registry_item.ready = registry_item.finished = true;
+		for (var i=0; i<registry_item.finished_listeners.length; i++) {
+			registry_item.finished_listeners[i]();
+		}
+		registry_item.ready_listeners = [];
+		registry_item.finished_listeners = [];
+	}
+
+	// make the request for a scriptha
+	function request_script(chain_opts,script_obj,registry_item,onload,preload_this_script) {
+		// setTimeout() "yielding" prevents some weird race/crash conditions in older browsers
+		setTimeout(function(){
+			var script, src = script_obj.real_src, xhr;
+			
+			// don't proceed until `append_to` is ready to append to
+			if ("item" in append_to) { // check if `append_to` ref is still a live node list
+				if (!append_to[0]) { // `append_to` node not yet ready
+					// try again in a little bit -- note: will re-call the anonymous function in the outer setTimeout, not the parent `request_script()`
+					setTimeout(arguments.callee,25);
+					return;
+				}
+				// reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists
+				append_to = append_to[0];
+			}
+			script = document.createElement("script");
+			if (script_obj.type) script.type = script_obj.type;
+			if (script_obj.charset) script.charset = script_obj.charset;
+			
+			// should preloading be used for this script?
+			if (preload_this_script) {
+				// real script preloading?
+				if (real_preloading) {
+					/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload: "+src);/*!END_DEBUG*/
+					registry_item.elem = script;
+					if (explicit_preloading) { // explicit preloading (aka, Zakas' proposal)
+						script.preload = true;
+						script.onpreload = onload;
+					}
+					else {
+						script.onreadystatechange = function(){
+							if (script.readyState == "loaded") onload();
+						};
+					}
+					script.src = src;
+					// NOTE: no append to DOM yet, appending will happen when ready to execute
+				}
+				// same-domain and XHR allowed? use XHR preloading
+				else if (preload_this_script && src.indexOf(root_domain) == 0 && chain_opts[_UseLocalXHR]) {
+					xhr = new XMLHttpRequest(); // note: IE never uses XHR (it supports true preloading), so no more need for ActiveXObject fallback for IE <= 7
+					/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (xhr): "+src);/*!END_DEBUG*/
+					xhr.onreadystatechange = function() {
+						if (xhr.readyState == 4) {
+							xhr.onreadystatechange = function(){}; // fix a memory leak in IE
+							registry_item.text = xhr.responseText + "\n//@ sourceURL=" + src; // http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
+							onload();
+						}
+					};
+					xhr.open("GET",src);
+					xhr.send();
+				}
+				// as a last resort, use cache-preloading
+				else {
+					/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (cache): "+src);/*!END_DEBUG*/
+					script.type = "text/cache-script";
+					create_script_load_listener(script,registry_item,"ready",function() {
+						append_to.removeChild(script);
+						onload();
+					});
+					script.src = src;
+					append_to.insertBefore(script,append_to.firstChild);
+				}
+			}
+			// use async=false for ordered async? parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
+			else if (script_ordered_async) {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load (ordered async): "+src);/*!END_DEBUG*/
+				script.async = false;
+				create_script_load_listener(script,registry_item,"finished",onload);
+				script.src = src;
+				append_to.insertBefore(script,append_to.firstChild);
+			}
+			// otherwise, just a normal script element
+			else {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load: "+src);/*!END_DEBUG*/
+				create_script_load_listener(script,registry_item,"finished",onload);
+				script.src = src;
+				append_to.insertBefore(script,append_to.firstChild);
+			}
+		},0);
+	}
+		
+	// create a clean instance of $LAB
+	function create_sandbox() {
+		var global_defaults = {},
+			can_use_preloading = real_preloading || xhr_or_cache_preloading,
+			queue = [],
+			registry = {},
+			instanceAPI
+		;
+		
+		// global defaults
+		global_defaults[_UseLocalXHR] = true;
+		global_defaults[_AlwaysPreserveOrder] = false;
+		global_defaults[_AllowDuplicates] = false;
+		global_defaults[_CacheBust] = false;
+		/*!START_DEBUG*/global_defaults[_Debug] = false;/*!END_DEBUG*/
+		global_defaults[_BasePath] = "";
+
+		// execute a script that has been preloaded already
+		function execute_preloaded_script(chain_opts,script_obj,registry_item) {
+			var script;
+			
+			function preload_execute_finished() {
+				if (script != null) { // make sure this only ever fires once
+					script = null;
+					script_executed(registry_item);
+				}
+			}
+			
+			if (registry[script_obj.src].finished) return;
+			if (!chain_opts[_AllowDuplicates]) registry[script_obj.src].finished = true;
+			
+			script = registry_item.elem || document.createElement("script");
+			if (script_obj.type) script.type = script_obj.type;
+			if (script_obj.charset) script.charset = script_obj.charset;
+			create_script_load_listener(script,registry_item,"finished",preload_execute_finished);
+			
+			// script elem was real-preloaded
+			if (registry_item.elem) {
+				registry_item.elem = null;
+			}
+			// script was XHR preloaded
+			else if (registry_item.text) {
+				script.onload = script.onreadystatechange = null;	// script injection doesn't fire these events
+				script.text = registry_item.text;
+			}
+			// script was cache-preloaded
+			else {
+				script.src = script_obj.real_src;
+			}
+			append_to.insertBefore(script,append_to.firstChild);
+
+			// manually fire execution callback for injected scripts, since events don't fire
+			if (registry_item.text) {
+				preload_execute_finished();
+			}
+		}
+	
+		// process the script request setup
+		function do_script(chain_opts,script_obj,chain_group,preload_this_script) {
+			var registry_item,
+				registry_items,
+				ready_cb = function(){ script_obj.ready_cb(script_obj,function(){ execute_preloaded_script(chain_opts,script_obj,registry_item); }); },
+				finished_cb = function(){ script_obj.finished_cb(script_obj,chain_group); }
+			;
+			
+			script_obj.src = canonical_uri(script_obj.src,chain_opts[_BasePath]);
+			script_obj.real_src = script_obj.src + 
+				// append cache-bust param to URL?
+				(chain_opts[_CacheBust] ? ((/\?.*$/.test(script_obj.src) ? "&_" : "?_") + ~~(Math.random()*1E9) + "=") : "")
+			;
+			
+			if (!registry[script_obj.src]) registry[script_obj.src] = {items:[],finished:false};
+			registry_items = registry[script_obj.src].items;
+
+			// allowing duplicates, or is this the first recorded load of this script?
+			if (chain_opts[_AllowDuplicates] || registry_items.length == 0) {
+				registry_item = registry_items[registry_items.length] = {
+					ready:false,
+					finished:false,
+					ready_listeners:[ready_cb],
+					finished_listeners:[finished_cb]
+				};
+
+				request_script(chain_opts,script_obj,registry_item,
+					// which callback type to pass?
+					(
+					 	(preload_this_script) ? // depends on script-preloading
+						function(){
+							registry_item.ready = true;
+							for (var i=0; i<registry_item.ready_listeners.length; i++) {
+								registry_item.ready_listeners[i]();
+							}
+							registry_item.ready_listeners = [];
+						} :
+						function(){ script_executed(registry_item); }
+					),
+					// signal if script-preloading should be used or not
+					preload_this_script
+				);
+			}
+			else {
+				registry_item = registry_items[0];
+				if (registry_item.finished) {
+					finished_cb();
+				}
+				else {
+					registry_item.finished_listeners.push(finished_cb);
+				}
+			}
+		}
+
+		// creates a closure for each separate chain spawned from this $LAB instance, to keep state cleanly separated between chains
+		function create_chain() {
+			var chainedAPI,
+				chain_opts = merge_objs(global_defaults,{}),
+				chain = [],
+				exec_cursor = 0,
+				scripts_currently_loading = false,
+				group
+			;
+			
+			// called when a script has finished preloading
+			function chain_script_ready(script_obj,exec_trigger) {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);/*!END_DEBUG*/
+				script_obj.ready = true;
+				script_obj.exec_trigger = exec_trigger;
+				advance_exec_cursor(); // will only check for 'ready' scripts to be executed
+			}
+
+			// called when a script has finished executing
+			function chain_script_executed(script_obj,chain_group) {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);/*!END_DEBUG*/
+				script_obj.ready = script_obj.finished = true;
+				script_obj.exec_trigger = null;
+				// check if chain group is all finished
+				for (var i=0; i<chain_group.scripts.length; i++) {
+					if (!chain_group.scripts[i].finished) return;
+				}
+				// chain_group is all finished if we get this far
+				chain_group.finished = true;
+				advance_exec_cursor();
+			}
+
+			// main driver for executing each part of the chain
+			function advance_exec_cursor() {
+				while (exec_cursor < chain.length) {
+					if (is_func(chain[exec_cursor])) {
+						/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);/*!END_DEBUG*/
+						try { chain[exec_cursor++](); } catch (err) {
+							/*!START_DEBUG*/if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);/*!END_DEBUG*/
+						}
+						continue;
+					}
+					else if (!chain[exec_cursor].finished) {
+						if (check_chain_group_scripts_ready(chain[exec_cursor])) continue;
+						break;
+					}
+					exec_cursor++;
+				}
+				// we've reached the end of the chain (so far)
+				if (exec_cursor == chain.length) {
+					scripts_currently_loading = false;
+					group = false;
+				}
+			}
+			
+			// setup next chain script group
+			function init_script_chain_group() {
+				if (!group || !group.scripts) {
+					chain.push(group = {scripts:[],finished:true});
+				}
+			}
+
+			// API for $LAB chains
+			chainedAPI = {
+				// start loading one or more scripts
+				script:function(){
+					for (var i=0; i<arguments.length; i++) {
+						(function(script_obj,script_list){
+							var splice_args;
+							
+							if (!is_array(script_obj)) {
+								script_list = [script_obj];
+							}
+							for (var j=0; j<script_list.length; j++) {
+								init_script_chain_group();
+								script_obj = script_list[j];
+								
+								if (is_func(script_obj)) script_obj = script_obj();
+								if (!script_obj) continue;
+								if (is_array(script_obj)) {
+									// set up an array of arguments to pass to splice()
+									splice_args = [].slice.call(script_obj); // first include the actual array elements we want to splice in
+									splice_args.unshift(j,1); // next, put the `index` and `howMany` parameters onto the beginning of the splice-arguments array
+									[].splice.apply(script_list,splice_args); // use the splice-arguments array as arguments for splice()
+									j--; // adjust `j` to account for the loop's subsequent `j++`, so that the next loop iteration uses the same `j` index value
+									continue;
+								}
+								if (typeof script_obj == "string") script_obj = {src:script_obj};
+								script_obj = merge_objs(script_obj,{
+									ready:false,
+									ready_cb:chain_script_ready,
+									finished:false,
+									finished_cb:chain_script_executed
+								});
+								group.finished = false;
+								group.scripts.push(script_obj);
+								
+								do_script(chain_opts,script_obj,group,(can_use_preloading && scripts_currently_loading));
+								scripts_currently_loading = true;
+								
+								if (chain_opts[_AlwaysPreserveOrder]) chainedAPI.wait();
+							}
+						})(arguments[i],arguments[i]);
+					}
+					return chainedAPI;
+				},
+				// force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding
+				wait:function(){
+					if (arguments.length > 0) {
+						for (var i=0; i<arguments.length; i++) {
+							chain.push(arguments[i]);
+						}
+						group = chain[chain.length-1];
+					}
+					else group = false;
+					
+					advance_exec_cursor();
+					
+					return chainedAPI;
+				}
+			};
+
+			// the first chain link API (includes `setOptions` only this first time)
+			return {
+				script:chainedAPI.script, 
+				wait:chainedAPI.wait, 
+				setOptions:function(opts){
+					merge_objs(opts,chain_opts);
+					return chainedAPI;
+				}
+			};
+		}
+
+		// API for each initial $LAB instance (before chaining starts)
+		instanceAPI = {
+			// main API functions
+			setGlobalDefaults:function(opts){
+				merge_objs(opts,global_defaults);
+				return instanceAPI;
+			},
+			setOptions:function(){
+				return create_chain().setOptions.apply(null,arguments);
+			},
+			script:function(){
+				return create_chain().script.apply(null,arguments);
+			},
+			wait:function(){
+				return create_chain().wait.apply(null,arguments);
+			},
+
+			// built-in queuing for $LAB `script()` and `wait()` calls
+			// useful for building up a chain programmatically across various script locations, and simulating
+			// execution of the chain
+			queueScript:function(){
+				queue[queue.length] = {type:"script", args:[].slice.call(arguments)};
+				return instanceAPI;
+			},
+			queueWait:function(){
+				queue[queue.length] = {type:"wait", args:[].slice.call(arguments)};
+				return instanceAPI;
+			},
+			runQueue:function(){
+				var $L = instanceAPI, len=queue.length, i=len, val;
+				for (;--i>=0;) {
+					val = queue.shift();
+					$L = $L[val.type].apply(null,val.args);
+				}
+				return $L;
+			},
+
+			// rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB
+			noConflict:function(){
+				global.$LAB = _$LAB;
+				return instanceAPI;
+			},
+
+			// create another clean instance of $LAB
+			sandbox:function(){
+				return create_sandbox();
+			}
+		};
+
+		return instanceAPI;
+	}
+
+	// create the main instance of $LAB
+	global.$LAB = create_sandbox();
+
+
+	/* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
+	   NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?).
+	   
+	   The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does 
+	   proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked 
+	   document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. 
+	   For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or 
+	   fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs.
+	*/ 
+	(function(addEvent,domLoaded,handler){
+		if (document.readyState == null && document[addEvent]){
+			document.readyState = "loading";
+			document[addEvent](domLoaded,handler = function(){
+				document.removeEventListener(domLoaded,handler,false);
+				document.readyState = "complete";
+			},false);
+		}
+	})("addEventListener","DOMContentLoaded");
+
+})(this);/*
+  mustache.js — Logic-less templates in JavaScript
+
+  See http://mustache.github.com/ for more info.
+*/
+
+var Mustache = function () {
+  var _toString = Object.prototype.toString;
+
+  Array.isArray = Array.isArray || function (obj) {
+    return _toString.call(obj) == "[object Array]";
+  }
+
+  var _trim = String.prototype.trim, trim;
+
+  if (_trim) {
+    trim = function (text) {
+      return text == null ? "" : _trim.call(text);
+    }
+  } else {
+    var trimLeft, trimRight;
+
+    // IE doesn't match non-breaking spaces with \s.
+    if ((/\S/).test("\xA0")) {
+      trimLeft = /^[\s\xA0]+/;
+      trimRight = /[\s\xA0]+$/;
+    } else {
+      trimLeft = /^\s+/;
+      trimRight = /\s+$/;
+    }
+
+    trim = function (text) {
+      return text == null ? "" :
+        text.toString().replace(trimLeft, "").replace(trimRight, "");
+    }
+  }
+
+  var escapeMap = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': '&quot;',
+    "'": '&#39;'
+  };
+
+  function escapeHTML(string) {
+    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
+      return escapeMap[s] || s;
+    });
+  }
+
+  var regexCache = {};
+  var Renderer = function () {};
+
+  Renderer.prototype = {
+    otag: "{{",
+    ctag: "}}",
+    pragmas: {},
+    buffer: [],
+    pragmas_implemented: {
+      "IMPLICIT-ITERATOR": true
+    },
+    context: {},
+
+    render: function (template, context, partials, in_recursion) {
+      // reset buffer & set context
+      if (!in_recursion) {
+        this.context = context;
+        this.buffer = []; // TODO: make this non-lazy
+      }
+
+      // fail fast
+      if (!this.includes("", template)) {
+        if (in_recursion) {
+          return template;
+        } else {
+          this.send(template);
+          return;
+        }
+      }
+
+      // get the pragmas together
+      template = this.render_pragmas(template);
+
+      // render the template
+      var html = this.render_section(template, context, partials);
+
+      // render_section did not find any sections, we still need to render the tags
+      if (html === false) {
+        html = this.render_tags(template, context, partials, in_recursion);
+      }
+
+      if (in_recursion) {
+        return html;
+      } else {
+        this.sendLines(html);
+      }
+    },
+
+    /*
+      Sends parsed lines
+    */
+    send: function (line) {
+      if (line !== "") {
+        this.buffer.push(line);
+      }
+    },
+
+    sendLines: function (text) {
+      if (text) {
+        var lines = text.split("\n");
+        for (var i = 0; i < lines.length; i++) {
+          this.send(lines[i]);
+        }
+      }
+    },
+
+    /*
+      Looks for %PRAGMAS
+    */
+    render_pragmas: function (template) {
+      // no pragmas
+      if (!this.includes("%", template)) {
+        return template;
+      }
+
+      var that = this;
+      var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
+        return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
+      });
+
+      return template.replace(regex, function (match, pragma, options) {
+        if (!that.pragmas_implemented[pragma]) {
+          throw({message:
+            "This implementation of mustache doesn't understand the '" +
+            pragma + "' pragma"});
+        }
+        that.pragmas[pragma] = {};
+        if (options) {
+          var opts = options.split("=");
+          that.pragmas[pragma][opts[0]] = opts[1];
+        }
+        return "";
+        // ignore unknown pragmas silently
+      });
+    },
+
+    /*
+      Tries to find a partial in the curent scope and render it
+    */
+    render_partial: function (name, context, partials) {
+      name = trim(name);
+      if (!partials || partials[name] === undefined) {
+        throw({message: "unknown_partial '" + name + "'"});
+      }
+      if (!context || typeof context[name] != "object") {
+        return this.render(partials[name], context, partials, true);
+      }
+      return this.render(partials[name], context[name], partials, true);
+    },
+
+    /*
+      Renders inverted (^) and normal (#) sections
+    */
+    render_section: function (template, context, partials) {
+      if (!this.includes("#", template) && !this.includes("^", template)) {
+        // did not render anything, there were no sections
+        return false;
+      }
+
+      var that = this;
+
+      var regex = this.getCachedRegex("render_section", function (otag, ctag) {
+        // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
+        return new RegExp(
+          "^([\\s\\S]*?)" +         // all the crap at the beginning that is not {{*}} ($1)
+
+          otag +                    // {{
+          "(\\^|\\#)\\s*(.+)\\s*" + //  #foo (# == $2, foo == $3)
+          ctag +                    // }}
+
+          "\n*([\\s\\S]*?)" +       // between the tag ($2). leading newlines are dropped
+
+          otag +                    // {{
+          "\\/\\s*\\3\\s*" +        //  /foo (backreference to the opening tag).
+          ctag +                    // }}
+
+          "\\s*([\\s\\S]*)$",       // everything else in the string ($4). leading whitespace is dropped.
+
+        "g");
+      });
+
+
+      // for each {{#foo}}{{/foo}} section do...
+      return template.replace(regex, function (match, before, type, name, content, after) {
+        // before contains only tags, no sections
+        var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
+
+        // after may contain both sections and tags, so use full rendering function
+            renderedAfter = after ? that.render(after, context, partials, true) : "",
+
+        // will be computed below
+            renderedContent,
+
+            value = that.find(name, context);
+
+        if (type === "^") { // inverted section
+          if (!value || Array.isArray(value) && value.length === 0) {
+            // false or empty list, render it
+            renderedContent = that.render(content, context, partials, true);
+          } else {
+            renderedContent = "";
+          }
+        } else if (type === "#") { // normal section
+          if (Array.isArray(value)) { // Enumerable, Let's loop!
+            renderedContent = that.map(value, function (row) {
+              return that.render(content, that.create_context(row), partials, true);
+            }).join("");
+          } else if (that.is_object(value)) { // Object, Use it as subcontext!
+            renderedContent = that.render(content, that.create_context(value),
+              partials, true);
+          } else if (typeof value == "function") {
+            // higher order section
+            renderedContent = value.call(context, content, function (text) {
+              return that.render(text, context, partials, true);
+            });
+          } else if (value) { // boolean section
+            renderedContent = that.render(content, context, partials, true);
+          } else {
+            renderedContent = "";
+          }
+        }
+
+        return renderedBefore + renderedContent + renderedAfter;
+      });
+    },
+
+    /*
+      Replace {{foo}} and friends with values from our view
+    */
+    render_tags: function (template, context, partials, in_recursion) {
+      // tit for tat
+      var that = this;
+
+      var new_regex = function () {
+        return that.getCachedRegex("render_tags", function (otag, ctag) {
+          return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g");
+        });
+      };
+
+      var regex = new_regex();
+      var tag_replace_callback = function (match, operator, name) {
+        switch(operator) {
+        case "!": // ignore comments
+          return "";
+        case "=": // set new delimiters, rebuild the replace regexp
+          that.set_delimiters(name);
+          regex = new_regex();
+          return "";
+        case ">": // render partial
+          return that.render_partial(name, context, partials);
+        case "{": // the triple mustache is unescaped
+        case "&": // & operator is an alternative unescape method
+          return that.find(name, context);
+        default: // escape the value
+          return escapeHTML(that.find(name, context));
+        }
+      };
+      var lines = template.split("\n");
+      for(var i = 0; i < lines.length; i++) {
+        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+        if (!in_recursion) {
+          this.send(lines[i]);
+        }
+      }
+
+      if (in_recursion) {
+        return lines.join("\n");
+      }
+    },
+
+    set_delimiters: function (delimiters) {
+      var dels = delimiters.split(" ");
+      this.otag = this.escape_regex(dels[0]);
+      this.ctag = this.escape_regex(dels[1]);
+    },
+
+    escape_regex: function (text) {
+      // thank you Simon Willison
+      if (!arguments.callee.sRE) {
+        var specials = [
+          '/', '.', '*', '+', '?', '|',
+          '(', ')', '[', ']', '{', '}', '\\'
+        ];
+        arguments.callee.sRE = new RegExp(
+          '(\\' + specials.join('|\\') + ')', 'g'
+        );
+      }
+      return text.replace(arguments.callee.sRE, '\\$1');
+    },
+
+    /*
+      find `name` in current `context`. That is find me a value
+      from the view object
+    */
+    find: function (name, context) {
+      name = trim(name);
+
+      // Checks whether a value is thruthy or false or 0
+      function is_kinda_truthy(bool) {
+        return bool === false || bool === 0 || bool;
+      }
+
+      var value;
+
+      // check for dot notation eg. foo.bar
+      if (name.match(/([a-z_]+)\./ig)) {
+        var childValue = this.walk_context(name, context);
+        if (is_kinda_truthy(childValue)) {
+          value = childValue;
+        }
+      } else {
+        if (is_kinda_truthy(context[name])) {
+          value = context[name];
+        } else if (is_kinda_truthy(this.context[name])) {
+          value = this.context[name];
+        }
+      }
+
+      if (typeof value == "function") {
+        return value.apply(context);
+      }
+      if (value !== undefined) {
+        return value;
+      }
+      // silently ignore unkown variables
+      return "";
+    },
+
+    walk_context: function (name, context) {
+      var path = name.split('.');
+      // if the var doesn't exist in current context, check the top level context
+      var value_context = (context[path[0]] != undefined) ? context : this.context;
+      var value = value_context[path.shift()];
+      while (value != undefined && path.length > 0) {
+        value_context = value;
+        value = value[path.shift()];
+      }
+      // if the value is a function, call it, binding the correct context
+      if (typeof value == "function") {
+        return value.apply(value_context);
+      }
+      return value;
+    },
+
+    // Utility methods
+
+    /* includes tag */
+    includes: function (needle, haystack) {
+      return haystack.indexOf(this.otag + needle) != -1;
+    },
+
+    // by @langalex, support for arrays of strings
+    create_context: function (_context) {
+      if (this.is_object(_context)) {
+        return _context;
+      } else {
+        var iterator = ".";
+        if (this.pragmas["IMPLICIT-ITERATOR"]) {
+          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+        }
+        var ctx = {};
+        ctx[iterator] = _context;
+        return ctx;
+      }
+    },
+
+    is_object: function (a) {
+      return a && typeof a == "object";
+    },
+
+    /*
+      Why, why, why? Because IE. Cry, cry cry.
+    */
+    map: function (array, fn) {
+      if (typeof array.map == "function") {
+        return array.map(fn);
+      } else {
+        var r = [];
+        var l = array.length;
+        for(var i = 0; i < l; i++) {
+          r.push(fn(array[i]));
+        }
+        return r;
+      }
+    },
+
+    getCachedRegex: function (name, generator) {
+      var byOtag = regexCache[this.otag];
+      if (!byOtag) {
+        byOtag = regexCache[this.otag] = {};
+      }
+
+      var byCtag = byOtag[this.ctag];
+      if (!byCtag) {
+        byCtag = byOtag[this.ctag] = {};
+      }
+
+      var regex = byCtag[name];
+      if (!regex) {
+        regex = byCtag[name] = generator(this.otag, this.ctag);
+      }
+
+      return regex;
+    }
+  };
+
+  return({
+    name: "mustache.js",
+    version: "0.5.0-dev",
+
+    /*
+      Turns a template and view into HTML
+    */
+    to_html: function (template, view, partials, send_fun) {
+      var renderer = new Renderer();
+      if (send_fun) {
+        renderer.send = send_fun;
+      }
+      renderer.render(template, view || {}, partials);
+      if (!send_fun) {
+        return renderer.buffer.join("\n");
+      }
+    }
+  });
+}();
+// Underscore.js 1.2.3
+// (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the MIT license.
+// Portions of Underscore are inspired or borrowed from Prototype,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore
+(function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
+c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c,
+h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.concat,H=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,I=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&&
+define.amd?define("underscore",function(){return b}):s._=b;b.VERSION="1.2.3";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(b,a[e],e,a)===o)break}else for(e in a)if(m.call(a,e)&&c.call(b,a[e],e,a)===o)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.map===w)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});return e};b.reduce=b.foldl=b.inject=function(a,
+c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(x&&a.reduce===x)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(y&&a.reduceRight===y)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,
+c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,c,b){var e;D(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.filter===z)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(A&&a.every===A)return a.every(c,
+b);j(a,function(a,g,h){if(!(e=e&&c.call(b,a,g,h)))return o});return e};var D=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(B&&a.some===B)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return q&&a.indexOf===q?a.indexOf(c)!=-1:b=D(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a,
+d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,
+computed:b})});return e.value};b.shuffle=function(a){var c=[],b;j(a,function(a,f){f==0?c[0]=a:(b=Math.floor(Math.random()*(f+1)),c[f]=c[b],c[b]=a)});return c};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,c){var b=a.criteria,d=c.criteria;return b<d?-1:b>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=
+function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-
+1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},
+[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,
+c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(q&&a.indexOf===q)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(C&&a.lastIndexOf===C)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
+var E=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));E.prototype=a.prototype;var b=new E,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
+c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return m.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
+a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=G.apply([a],arguments);return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=
+function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=I||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments,
+1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===
+Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)==
+"[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),function(c){J(c,
+b[c]=a[c])})};var K=0;b.uniqueId=function(a){var b=K++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,
+"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},J=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);H.call(a,this._wrapped);return u(c.apply(b,
+a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped,arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this);
+/* main file */
+
+
+if ( window.IriSP === undefined && window.__IriSP === undefined ) { 
+  /**
+    @class
+    the object under which everything goes.        
+  */
+	IriSP = {}; 
+  
+  /** Alias to IriSP for backward compatibility */
+	__IriSP = IriSP;
+}
+
+/* underscore comes bundled with the player and we need 
+   it ASAP, so load it that way
+*/
+
+IriSP._ = window._.noConflict();
+IriSP.underscore = IriSP._;
+
+IriSP.loadLibs = function( libs, config, metadata_url, callback ) {
+    // Localize jQuery variable
+		IriSP.jQuery = null;
+    var $L = $LAB.script(libs.jQuery).script(libs.swfObject).wait()
+                .script(libs.jQueryUI);
+                                   
+    if (config.player.type === "jwplayer") {
+      // load our popcorn.js lookalike
+      $L = $L.script(libs.jwplayer);
+    } else {
+      // load the real popcorn
+      $L = $L.script(libs.popcorn).script(libs["popcorn.code"]);
+      if (config.player.type === "youtube") {
+        $L = $L.script(libs["popcorn.youtube"]);
+      } 
+      if (config.player.type === "vimeo")
+        $L = $L.script(libs["popcorn.vimeo"]);
+      
+      /* do nothing for html5 */
+    }       
+    
+    /* widget specific requirements */
+    for (var idx in config.gui.widgets) {
+      if (config.gui.widgets[idx].type === "PolemicWidget" ||
+          config.gui.widgets[idx].type === "StackGraphWidget") {        
+        $L.script(libs.raphael);
+      }
+
+      if (config.gui.widgets[idx].type === "SparklineWidget") {
+        $L.script(libs.jquery_sparkline);
+      }
+    }
+    
+    // same for modules
+    /*
+    for (var idx in config.modules) {
+      if (config.modules[idx].type === "PolemicWidget")
+        $L.script(libs.raphaelJs);
+    }
+    */
+
+    $L.wait(function() {
+      IriSP.jQuery = window.jQuery.noConflict( true );
+      
+      var css_link_jquery = IriSP.jQuery( "<link>", { 
+        rel: "stylesheet", 
+        type: "text/css", 
+        href: libs.cssjQueryUI,
+        'class': "dynamic_css"
+      } );
+      var css_link_custom = IriSP.jQuery( "<link>", { 
+        rel: "stylesheet", 
+        type: "text/css", 
+        href: config.gui.css,
+        'class': "dynamic_css"
+      } );
+      
+      css_link_jquery.appendTo('head');
+      css_link_custom.appendTo('head');
+          
+      IriSP.setupDataLoader();
+      IriSP.__dataloader.get(metadata_url, 
+          function(data) {
+            /* save the data so that we could re-use it to
+               configure the video
+            */
+            IriSP.__jsonMetadata = data;
+            callback.call(window) });
+    });
+};
+IriSP.SparklineWidget_template = "<div class='Ldt-sparklineWidget' style='width: {{width}}px; height: {{height}}px'>    <div class='Ldt-sparkLinePositionMarker' style='width: 0px; height: {{height}}px'></div>    <div class='Ldt-sparkLineClickOverlay' style='width: {{width}}px; height: {{height}}px'></div>    <div class='Ldt-sparkLine' style='width: {{width}}px; height: {{height}}px'>Loading</div></div>";
+IriSP.annotation_template = "{{! template for an annotation displayed in a segmentWidget }}<div title='{{divTitle}}' id='{{id}}'	class='Ldt-iri-chapter' 	style='left: {{startPixel}}px;          width: {{pxWidth}}px;          background-color:#{{hexa_color}};' 	></div>";
+IriSP.annotationWidget_template = "{{! template for the annotation widget }}<div class='Ldt-AnnotationsWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-Annotation-DoubleBorder'>      <div class='Ldt-AnnotationContent'>          <div class='Ldt-AnnotationShareIcons'>         <a target='_blank' class='Ldt-fbShare' title='share on facebook'></a>         <a target='_blank' class='Ldt-TwShare' title='share on twitter'></a>         <a target='_blank'  class='Ldt-GplusShare' title='share on google+'></a>        </div>        <div class='Ldt-SaTitle'></div>        <div class='Ldt-SaDescription'></div>        <div class='Ldt-SaKeywords'></div>    </div>  </div></div>";
+IriSP.annotation_loading_template = "{{! template shown while the annotation widget is loading }}<div id='Ldt-load-container'><div id='Ldt-loader'>&nbsp;</div> Chargement... </div>";
+IriSP.annotationsListWidget_template = "{{! template for the annotation list widget }}<div class='Ldt-AnnotationsListWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-Annotation-DoubleBorder'>    <ul>    {{#annotations}}      <li>        {{! if the url is not present, it means that the annotation exists             in the current project }}        {{^url}}        <a href='#id={{id}}'>        {{/url}}        {{! otherwise link to url }}        {{#url}}        <a href='{{url}}#id={{id}}'>        {{/url}}          <div style='overflow: auto; margin-top: 5px; margin-bottom: 5px;'>            <div class='Ldt-AnnotationsList-Caption'>            </div>            <div class='Ldt-AnnotationsList-Duration'>{{begin}} - {{end}}</div>            <div class='Ldt-AnnotationsList-Title'>{{title}}</div>            <div class='Ldt-AnnotationsList-Description'>{{desc}}</div>          </div>        </a>      </li>    {{/annotations}}    </ul>  </div></div>";
+IriSP.arrowWidget_template = "<div class='Ldt-arrowWidget Ldt-arrowLeftEdge'></div>";
+IriSP.createAnnotationWidget_template = "{{! template for the annotation creation widget }}<div class='Ldt-createAnnotationWidget'>  <!-- ugly div because we want to have a double border -->  <div class='Ldt-createAnnotation-DoubleBorder'>    <div class='Ldt-createAnnotation-startScreen'>      <div style='margin-bottom: 7px; overflow: auto;'>        <div class='Ldt-createAnnotation-Title'></div>        <div class='Ldt-createAnnotation-TimeFrame'></div>        {{^cinecast_version}}          <div class='Ldt-createAnnotation-Minimize' title='Cancel'></div>        {{/cinecast_version}}      </div>            <div class='Ldt-createAnnotation-Container'>        <textarea class='Ldt-createAnnotation-Description'></textarea>        <div class='Ldt-createAnnotation-profileArrow'>                  </div>        <div class='Ldt-createAnnotation-userAvatar'>        {{^user_avatar}}          <img src='https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png'></img>        {{/user_avatar}}        {{#user_avatar}}          <img src='{{ user_avatar }}'></img>        {{/user_avatar}}        </div>      </div>      <div class='Ldt-createAnnotation-submitButton'>        <div style='position: absolute; bottom: 5px; right: 5px;'>Submit</div>      </div>      <div class='Ldt-createAnnotation-keywords'>        Add keywords :             </div>      {{#polemic_mode}}      <div class='Ldt-createAnnotation-polemics'>        Add polemic keywords           </div>      {{/polemic_mode}}    </div>    <div class='Ldt-createAnnotation-waitScreen' style='display: none; text-align: center'>      <div class='Ldt-createAnnotation-spinner'></div>      Please wait while your request is being processed...    </div>    <div class='Ldt-createAnnotation-errorScreen' style='display: none; text-align: center'>      <div class='Ldt-createAnnotation-Minimize' title='Hide'></div>      An error happened while contacting the server. Your annotation has not been saved.    </div>        <div class='Ldt-createAnnotation-endScreen' style='display: none'>      <div class='Ldt-createAnnotation-Minimize' title='Hide'></div>      Thank you, your annotation has been saved.<br>      Would you like to share it on social networks ?      <div style='margin-top: 12px; text-align: center;'>          <a target='_blank' class='Ldt-createAnnotation-endScreen-TweetLink'></a>          <a target='_blank' class='Ldt-createAnnotation-endScreen-FbLink'></a>          <a target='_blank' class='Ldt-createAnnotation-endScreen-GplusLink'></a>                          </div>    </div>  </div></div>";
+IriSP.createAnnotation_errorMessage_template = "<p class='Ldt-createAnnotation-errorMessage'>  You must enter text to submit an annotation</p>";
+IriSP.overlay_marker_template = "{{! the template for the small bars which is z-indexed over our segment widget }}<div class='Ldt-SegmentPositionMarker' style='background-color: #F7268E;'></div>";
+IriSP.player_template = "{{! template for the radio player }}<div class='Ldt-controler demo'>	<div class='Ldt-LeftPlayerControls'>    <div class='Ldt-button Ldt-CtrlPlay Ldt-CtrlPlay-PlayState' title='Play/Pause'></div>		<div class='Ldt-button Ldt-CtrlAnnotate' title='Annotate'></div>    <div class='Ldt-button Ldt-CtrlSearch' title='Search'></div>    <div class='LdtSearch'>      <input class='LdtSearchInput' style='margin-top: 2px; margin-bottom: 2px;'></input>    </div>    	</div>	<div class='Ldt-RightPlayerControls'>    <div class='Ldt-Time'>      <div class='Ldt-ElapsedTime' title='Elapsed time'>00:00</div>      <div class='Ldt-TimeSeparator'>/</div>      <div class='Ldt-TotalTime' title='Total time'>00:00</div>    </div>		<div class='Ldt-button Ldt-CtrlSound Ldt-CtrlSound-MuteState' title='Mute/Unmute'></div>	</div></div>";
+IriSP.search_template = "{{! template for the search container }}<div class='LdtSearchContainer'	style='margin-left: {{margin_left}}; position: absolute; margin-top: -60px;'>	<div class='LdtSearch'		style='display: none; background-color: #EEE; width: 165px; border-color: #CFCFCF; position: absolute; text-align: center;'>		<input class='LdtSearchInput'			style='margin-top: 1px; margin-bottom: 2px;' />	</div></div><div class='cleaner'></div>";
+IriSP.share_template = "{{! social network sharing template }}<a onclick='__IriSP.MyApiPlayer.share(\'delicious\');' title='partager avec delicious'><span class='share shareDelicious'>&nbsp;</span></a>		<a onclick='__IriSP.MyApiPlayer.share(\'facebook\');' title='partager avec facebook'> <span class='share shareFacebook'>&nbsp;</span></a><a onclick='__IriSP.MyApiPlayer.share(\'twitter\');' title='partager avec twitter'>  <span class='share shareTwitter'>&nbsp;</span></a><a onclick='__IriSP.MyApiPlayer.share(\'myspace\');' title='partager avec Myspace'>  <span class='share shareMySpace'>&nbsp;</span></a>";
+IriSP.sliceWidget_template = "{{! template for the slice widget }}<div class='Ldt-sliceWidget'>  {{! the whole bar }}  <div class='Ldt-sliceBackground'></div>    <div class='Ldt-sliceLeftHandle'></div>  {{! the zone which represents our slice }}  <div class='Ldt-sliceZone'></div>     <div class='Ldt-sliceRightHandle'></div></div>";
+IriSP.sliderWidget_template = "{{! template for the slider widget - it's composed of two divs we one overlayed on top    of the other }}<div class='Ldt-sliderBackground'></div><div class='Ldt-sliderForeground'></div><div class='Ldt-sliderPositionMarker'></div>";
+IriSP.tooltip_template = "{{! template used by the jquery ui tooltip }}<div class='Ldt-tooltip'>  <div class='title'>{{title}}</div>  <div class='time'>{{begin}} : {{end}} </div>  <div class='description'>{{description}}</div></div>";
+IriSP.tooltipWidget_template = "{{! template for the tooltip widget }}<div class='tip'>	<div class='tipcolor' style='height:10px;width:10px'></div>	<div class='tiptext'></div>";
+IriSP.tweetWidget_template = "{{! template for the tweet widget }}<div class='Ldt-tweetWidget'>  <div class='Ldt-tweet-DoubleBorder'>      <div class='Ldt-tweetWidgetKeepOpen' title='dont minimize automatically'></div>      <div class='Ldt-tweetWidgetMinimize' title='minimize window'></div>      <div class='Ldt-tweetAvatar'></div>      <div class='Ldt-tweetAvatar-profileArrow'></div>      <div class='Ldt-tweetContents'></div>      <a href='' target='_blank' class='Ldt-Retweet'><div class='Ldt-RetweetIcon'></div> - Retweet </a>      <a href='' target='_blank' class='Ldt-TweetReply'><div class='Ldt-TweetReplyIcon'></div> - Reply</a>  </div></div>";/* utils.js - various utils that don't belong anywhere else */
+
+/* trace function, for debugging */
+
+IriSP.traceNum = 0;
+IriSP.trace = function( msg, value ) {
+/*
+	if( IriSP.config.gui.debug === true ) {
+		IriSP.traceNum += 1;
+		IriSP.jQuery( "<div>"+IriSP.traceNum+" - "+msg+" : "+value+"</div>" ).appendTo( "#Ldt-output" );
+	}
+*/
+};
+
+/* used in callbacks - because in callbacks we lose "this",
+   we need to have a special function which wraps "this" in 
+   a closure. This way, the 
+*/   
+IriSP.wrap = function (obj, fn) {
+  return function() {    
+    var args = Array.prototype.slice.call(arguments, 0);
+    return fn.apply(obj, args);
+  }
+}
+
+/* convert a time to a percentage in the media */
+IriSP.timeToPourcent = function(time, timetotal){
+	var time = Math.abs(time);
+  var timetotal = Math.abs(timetotal);
+  
+	return Math.floor((time/timetotal) * 100);
+};
+
+IriSP.padWithZeros = function(num) {
+  if (Math.abs(num) < 10) {
+    return "0" + num.toString();
+  } else {
+    return num.toString();
+  }
+};
+
+/* convert a number of milliseconds to a tuple of the form 
+   [hours, minutes, seconds]
+*/
+IriSP.msToTime = function(ms) {
+  return IriSP.secondsToTime(ms / 1000);
+}
+/* convert a number of seconds to a tuple of the form 
+   [hours, minutes, seconds]
+*/
+IriSP.secondsToTime = function(secs) {  
+  var hours = Math.abs(parseInt( secs / 3600 ) % 24);
+  var minutes = Math.abs(parseInt( secs / 60 ) % 60);
+  var seconds = parseFloat(Math.abs(secs % 60).toFixed(0));
+  
+  var toString_fn = function() {
+    var ret = "";
+    if (hours > 0)
+       ret = IriSP.padWithZeros(this.hours) + ":";
+    ret += IriSP.padWithZeros(this.minutes) + ":" + IriSP.padWithZeros(this.seconds);
+
+    return ret;
+  }
+  return {"hours" : hours, "minutes" : minutes, "seconds" : seconds, toString: toString_fn};
+};
+
+/* format a tweet - replaces @name by a link to the profile, #hashtag, etc. */
+IriSP.formatTweet = function(tweet) {
+  /*
+    an array of arrays which hold a regexp and its replacement.
+  */
+  var regExps = [
+    /* copied from http://codegolf.stackexchange.com/questions/464/shortest-url-regex-match-in-javascript/480#480 */
+    [/((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)/gi, "<a href='$1'>$1</a>"],
+    [/@(\w+)/gi, "<a href='http://twitter.com/$1'>@$1</a>"], // matches a @handle
+    [/#(\w+)/gi, "<a href='http://twitter.com/search?q=%23$1'>#$1</a>"],// matches a hashtag
+    [/(\+\+)/gi, "<span class='Ldt-PolemicPlusPlus'>$1</span>"],
+    [/(--)/gi, "<span class='Ldt-PolemicMinusMinus'>$1</span>"],
+    [/(==)/gi, "<span class='Ldt-PolemicEqualEqual'>$1</span>"],
+    [/(\?\?)/gi, "<span class='Ldt-PolemicQuestion'>$1</span>"]
+  ]; 
+
+  var i = 0;
+  for(i = 0; i < regExps.length; i++) {
+     tweet = tweet.replace(regExps[i][0], regExps[i][1]);
+  }
+  
+  return tweet;
+};
+
+IriSP.countProperties = function(obj) {
+    var count = 0;
+
+    for(var prop in obj) {
+        if(obj.hasOwnProperty(prop))
+                ++count;
+    }
+
+    return count;
+};
+
+// conversion de couleur Decimal vers HexaDecimal || 000 si fff
+IriSP.DEC_HEXA_COLOR = function (dec) {
+  var val = +dec;
+  var str = val.toString(16);
+  var zeroes = "";
+  if (str.length < 6) {
+    for (var i = 0; i < 6 - str.length; i++)
+      zeroes += "0";
+  }
+  return zeroes + str;
+};
+
+/* shortcut to have global variables in templates */
+IriSP.templToHTML = function(template, values) {
+  var params = IriSP.jQuery.extend(IriSP.default_templates_vars, values);
+  return Mustache.to_html(template, params);
+};
+
+/* we need to be stricter than encodeURIComponent,
+   because of twitter
+*/  
+IriSP.encodeURI = function(str) {
+  return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').  
+                                 replace(/\)/g, '%29').replace(/\*/g, '%2A');  
+}  
+
+IriSP.__guidCounter = 0;
+IriSP.guid = function(prefix) {
+  IriSP.__guidCounter += 1;
+  return prefix + IriSP.__guidCounter;
+};
+
+/** returns an url to share on facebook */
+IriSP.mkFbUrl = function(url, text) {
+  if (typeof(text) === "undefined")
+    text = "I'm watching ";
+  
+  return "http://www.facebook.com/share.php?u=" + IriSP.encodeURI(text) + IriSP.shorten_url(url);
+};
+
+/** returns an url to share on twitter */
+IriSP.mkTweetUrl = function(url, text) {
+  if (typeof(text) === "undefined")
+    text = "I'm watching ";
+  
+  return "http://twitter.com/home?status=" + IriSP.encodeURI(text) + IriSP.shorten_url(url);
+};
+
+/** returns an url to share on google + */
+IriSP.mkGplusUrl = function(url, text) {
+  return "https://plusone.google.com/_/+1/confirm?hl=en&url=" + IriSP.shorten_url(url);
+};
+
+/** test if a value is null or undefined */
+IriSP.null_or_undefined = function(val) {
+  return (typeof(val) === "undefined" || val === null);
+};
+
+/** issue a call to an url shortener and return the shortened url */
+IriSP.shorten_url = function(url) {
+  if (IriSP.config.shortener.hasOwnProperty("shortening_function"))
+    return IriSP.config.shortener.shortening_function(url);
+    
+  return url;
+};
+
+/** Similar to substr but remove the last word if
+    we're breaking a word in two.
+ */ 
+IriSP.clean_substr = function(str, start, end) {
+  var s = str.substr(start, end).substr(start, end).split(" ");
+  s.pop();
+  return s.join(" ");
+};
+/* for ie compatibility
+if (Object.prototype.__defineGetter__&&!Object.defineProperty) {
+   Object.defineProperty=function(obj,prop,desc) {
+      if ("get" in desc) obj.__defineGetter__(prop,desc.get);
+      if ("set" in desc) obj.__defineSetter__(prop,desc.set);
+   }
+}
+*/
+/* wrapper that simulates popcorn.js because
+   popcorn is a bit unstable at the time */
+
+IriSP.PopcornReplacement = {  
+};
+
+/** base class for our popcorn-compatible players.
+ */
+IriSP.PopcornReplacement.player = function(container, options) {
+  /* the jwplayer calls the callbacks in the global space so we need to 
+     preserve them using IriSP.wrap */
+  this.callbacks = {
+      onReady:  IriSP.wrap(this, this.__initApi),
+      onTime:   IriSP.wrap(this, this.__timeHandler),
+      onPlay:   IriSP.wrap(this, this.__playHandler),
+      onPause:  IriSP.wrap(this, this.__pauseHandler),
+      onSeek:   IriSP.wrap(this, this.__seekHandler) 
+  };
+  
+  this.media = { 
+    "paused": true,
+    "muted": false
+  };
+    
+  this.container = container.slice(1); //eschew the '#'
+  
+  this.msgPump = {}; /* dictionnary used to receive and send messages */
+  this.__codes = []; /* used to schedule the execution of a piece of code in 
+                        a segment (similar to the popcorn.code plugin). */
+  
+  this._options = options;
+                          
+};
+
+IriSP.PopcornReplacement.player.prototype.listen = function(msg, callback) {
+  if (!this.msgPump.hasOwnProperty(msg))
+    this.msgPump[msg] = [];
+
+  this.msgPump[msg].push(callback);
+};
+
+IriSP.PopcornReplacement.player.prototype.trigger = function(msg, params) {
+  if (!this.msgPump.hasOwnProperty(msg))
+    return;
+
+  var d = this.msgPump[msg];
+
+  for(var i = 0; i < d.length; i++) {
+    d[i].call(window, params);
+  }
+
+};
+
+IriSP.PopcornReplacement.player.prototype.guid = function(prefix) {
+  var str = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+      var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
+      return v.toString(16);
+   });
+
+  return prefix + str;
+};
+
+/** init the api after that flash player has been setup - called by the callback
+    defined by the embedded flash player 
+*/
+IriSP.PopcornReplacement.player.prototype.__initApi = function() {
+  this.trigger("loadedmetadata"); // we've done more than loading metadata of course,
+                                                      // but popcorn doesn't need to know more.
+  this.media.muted = this.playerFns.getMute();
+  /* some programmed segments are supposed to be run at the beginning */
+  var i = 0;
+  for(i = 0; i < this.__codes.length; i++) {
+    var c = this.__codes[i];
+    if (0 == c.start) {
+      c.onStart();
+    }
+    
+    if (0 == c.end) {
+      c.onEnd();
+    }
+  }
+};
+
+/*
+IriSP.PopcornReplacement.jwplayer = function(container, options) {
+  IriSP.PopcornReplacement._container = container.slice(1); //eschew the '#'
+  options.events = {
+      onReady: IriSP.PopcornReplacement.__initApi,
+      onTime: IriSP.PopcornReplacement.__timeHandler,
+      onPlay: IriSP.PopcornReplacement.__playHandler,
+      onPause: IriSP.PopcornReplacement.__pauseHandler,
+      onSeek: IriSP.PopcornReplacement.__seekHandler 
+      }
+    
+  jwplayer(IriSP.PopcornReplacement._container).setup(options);
+  IriSP.PopcornReplacement.media.duration = options.duration;
+  return IriSP.PopcornReplacement;
+};
+*/
+
+IriSP.PopcornReplacement.player.prototype.currentTime = function(time) {
+  if (typeof(time) === "undefined") {        
+      return this.playerFns.getPosition();            
+  } else {
+     var currentTime = +time;
+     this.playerFns.seek(currentTime);              
+     return currentTime;
+  }
+};
+
+IriSP.PopcornReplacement.player.prototype.play = function() {
+  this.media.paused = false;
+  this.trigger("play");
+  //IriSP.PopcornReplacement.trigger("playing");
+  this.playerFns.play();
+};
+    
+IriSP.PopcornReplacement.player.prototype.pause = function() {
+  if ( !this.media.paused ) {
+    this.media.paused = true;
+    this.trigger( "pause" );
+    this.playerFns.pause();
+  }
+};
+
+IriSP.PopcornReplacement.player.prototype.muted = function(val) {
+  if (typeof(val) !== "undefined") {
+
+    if (this.playerFns.getMute() !== val) {
+      if (val) {
+        this.playerFns.setMute(true);
+        this.media.muted = true;
+      } else {
+        this.playerFns.setMute(false);
+        this.media.muted = false;
+      }
+
+      this.trigger( "volumechange" );
+    }
+    
+    return this.playerFns.getMute();
+  } else {
+    return this.playerFns.getMute();
+  }
+};
+
+IriSP.PopcornReplacement.player.prototype.mute = IriSP.PopcornReplacement.player.prototype.muted;
+
+IriSP.PopcornReplacement.player.prototype.code = function(options) {
+  this.__codes.push(options);
+  return this;
+};
+
+/* called everytime the player updates itself 
+   (onTime event)
+ */
+
+IriSP.PopcornReplacement.player.prototype.__timeHandler = function(event) {
+  var pos = event.position;
+
+  var i = 0;
+  for(i = 0; i < this.__codes.length; i++) {
+     var c = this.__codes[i];
+
+     if (pos >= c.start && pos < c.end && 
+         pos - 1 <= c.start) {       
+        c.onStart();
+     }
+ 
+     if (pos > c.start && pos > c.end && 
+         pos - 1 <= c.end) {
+         c.onEnd();
+     }
+   
+  }
+ 
+  this.trigger("timeupdate");
+};
+
+IriSP.PopcornReplacement.player.prototype.__seekHandler = function(event) {
+  var i = 0;
+  
+  for(i = 0; i < this.__codes.length; i++) {
+     var c = this.__codes[i];
+    
+     if (event.position >= c.start && event.position < c.end) {        
+        c.onEnd();
+     }         
+   }
+  
+   for(i = 0; i < this.__codes.length; i++) {
+     var c = this.__codes[i];
+
+     if (typeof(event.offset) === "undefined")
+       event.offset = 0;
+           
+     if (event.offset >= c.start && event.offset < c.end) { 
+       c.onStart();
+     }
+     
+   }
+  
+  /* this signal sends as an extra argument the position in the video.
+     As far as I know, this argument is not provided by popcorn */
+  this.trigger("seeked", event.offset);  
+};
+
+IriSP.PopcornReplacement.player.prototype.__playHandler = function(event) {
+  this.media.paused = false;
+  this.trigger("play");
+};
+
+IriSP.PopcornReplacement.player.prototype.__pauseHandler = function(event) {
+  this.media.paused = true;
+  this.trigger("pause");
+};
+
+IriSP.PopcornReplacement.player.prototype.roundTime = function() {
+  var currentTime = this.currentTime();
+  return Math.round(currentTime);
+};/* data.js - this file deals with how the players gets and sends data */
+
+IriSP.DataLoader = function() {
+  this._cache = {};
+  
+  /*
+    A structure to hold callbacks for specific urls. We need it because
+    ajax calls are asynchronous, so it means that sometimes we ask
+    multiple times for a ressource because the first call hasn't been
+    received yet.
+  */
+  this._callbacks = {};
+};
+
+IriSP.DataLoader.prototype.get = function(url, callback) {
+
+  var base_url = url.split("&")[0]
+  if (this._cache.hasOwnProperty(base_url)) {
+    callback(this._cache[base_url]);
+  } else {  
+    if (!this._callbacks.hasOwnProperty(base_url)) {
+      this._callbacks[base_url] = [];
+      this._callbacks[base_url].push(callback);   
+      /* we need a closure because this gets lost when it's called back */
+  
+      // uncomment you don't want to use caching.
+      // IriSP.jQuery.get(url, callback);
+      
+      var func = function(data) {
+                  this._cache[base_url] = data;                                
+                  var i = 0;
+                  
+                  for (i = 0; i < this._callbacks[base_url].length; i++) {
+                    this._callbacks[base_url][i](this._cache[base_url]);                                  
+                  }
+      };
+      
+      /* automagically choose between json and jsonp */
+      if (url.indexOf(document.location.hostname) === -1 &&
+          url.indexOf("http://") !== -1 /* not a relative url */ ) {
+        // we contacting a foreign domain, use JSONP
+
+        IriSP.jQuery.get(url, {}, IriSP.wrap(this, func), "jsonp");
+      } else {
+
+        // otherwise, hey, whatever rows your boat
+        IriSP.jQuery.get(url, IriSP.wrap(this, func));
+      }
+    
+    } else {
+      /* simply push the callback - it'll get called when the ressource
+         has been received */
+      
+      this._callbacks[base_url].push(callback);   
+   
+    }
+  }
+}
+
+/* the base abstract "class" */
+IriSP.Serializer = function(DataLoader, url) {
+  this._DataLoader = DataLoader;
+  this._url = url;
+  this._data = [];
+};
+
+IriSP.Serializer.prototype.serialize = function(data) { };
+IriSP.Serializer.prototype.deserialize = function(data) {};
+
+IriSP.Serializer.prototype.currentMedia = function() {  
+};
+
+IriSP.Serializer.prototype.sync = function(callback) {  
+  callback.call(this, this._data);  
+};
+
+IriSP.SerializerFactory = function(DataLoader) {
+  this._dataloader = DataLoader;
+};
+
+IriSP.SerializerFactory.prototype.getSerializer = function(metadataOptions) {
+  /* This function returns serializer set-up with the correct
+     configuration - takes a metadata struct describing the metadata source
+  */
+  
+  if (metadataOptions === undefined)
+    /* return an empty serializer */
+    return IriSP.Serializer("", "");
+            
+  switch(metadataOptions.type) {
+    case "json":
+      return new IriSP.JSONSerializer(this._dataloader, metadataOptions.src);
+      break;
+    
+    case "dummy": /* only used for unit testing - not defined in production */
+      return new IriSP.MockSerializer(this._dataloader, metadataOptions.src);
+      break;
+    
+    case "empty":
+      return new IriSP.Serializer("", "empty");
+      break;
+      
+    default:      
+      return undefined;
+  }
+};
+/* site.js - all our site-dependent config : player chrome, cdn locations, etc...*/
+
+IriSP.defaults = {};
+
+/* these objects are filled by configureDefaults. The function doesn't overwrite 
+   defaults that were originally defined by the user.
+*/
+IriSP.lib = {};
+
+/* We need to define those so that the individual settings can be overwritten */
+IriSP.widgetsDefaults = {};
+
+IriSP.paths = {};
+
+IriSP.libdir = "/metadataplayer/src/js/libs/";
+IriSP.jwplayer_swf_path = "/metadataplayer/test/libs/player.swf";
+IriSP.platform_url = "http://localhost/pf";
+IriSP.default_templates_vars = { };
+
+/** ugly ugly ugly ugly - returns an object defining 
+    the paths to the libs
+    We need it that way cause it's called at runtime by
+    IriSP.configureDefaults.
+*/   
+IriSP.defaults.lib = function(libdir) {
+  if (IriSP.null_or_undefined(libdir))
+    libdir = IriSP.libdir;
+  
+  return { 
+      jQuery : "http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js",
+      jQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.17/jquery-ui.js",
+      jQueryToolTip : "http://cdn.jquerytools.org/1.2.4/all/jquery.tools.min.js",
+      swfObject : "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js",
+      cssjQueryUI : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/base/jquery-ui.css",
+      popcorn : libdir + "popcorn.js",
+      jwplayer : libdir + "jwplayer.js",
+      popcornReplacement: libdir + "pop.js",
+      raphael: libdir + "raphael.js",
+      jquery_sparkline: libdir + "jquery.sparkline.js",
+      "popcorn.mediafragment" : libdir + "popcorn.mediafragment.js",
+      "popcorn.code" : libdir + "popcorn.code.js",
+      "popcorn.jwplayer": libdir + "popcorn.jwplayer.js",
+      "popcorn.youtube": libdir + "popcorn.youtube.js"    
+  };
+};
+
+//Configuration for the player and utility functions.
+// No need to have them configured at runtime
+IriSP.config = {};
+
+IriSP.config.shortener = {
+  // function to call to shorten an url.
+  //shortening_function : IriSP.platform_shorten_url
+};
+
+IriSP.defaults.widgetsDefaults = function(platform_url) { 
+  if (IriSP.null_or_undefined(platform_url))
+    platform_url = IriSP.platform_url;
+  
+  return {
+    "LayoutManager" : {spacer_div_height : "0px" },
+    "PlayerWidget" : {},
+    "AnnotationsWidget": {
+      "share_text" : "I'm watching ",     
+      "fb_link" : "http://www.facebook.com/share.php?u=",
+      "tw_link" : "http://twitter.com/home?status=",
+      "gplus_link" : ""
+      },
+    
+    "TweetsWidget" : {
+        default_profile_picture : "https://si0.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
+        tweet_display_period: 10000 // how long do we show a tweet ?
+        
+    },
+    "SliderWidget" : {
+        minimize_period: 850 // how long does the slider stays maximized after the user leaves the zone ?
+    },
+    "createAnnotationWidget" : {
+        keywords: ["#faux-raccord", "#mot-clef"],
+        polemic_mode: true, /* enable polemics ? */
+        /* polemics - the corresponding class names defined in the css should be for instance :
+           Ldt-createAnnotation-polemic-plusplus for plusplus
+           Ldt-createAnnotation-polemic-equalequal for equalequal, etc.
+        */
+        polemics: {"++" : "positive", "--" : "negative", "==" : "reference", "??" : "question"}, 
+        cinecast_version: true /* put to false to enable the platform version, true for the festival cinecast one. */
+    },
+    "SparklineWidget" : {
+        column_width: 10 // the width of a column in pixels.
+    },
+    "Main" : {
+        autoplay: true
+    },
+    "AnnotationsListWidget" : {
+        ajax_mode: true, /* use ajax to get information about the annotations.
+                           if set to false, only search in the annotations for the
+                           current project. */
+        /* the platform generates some funky urls. We replace them afterwards to point to the
+           correct place - this setting will probably be overwritten by the platform 
+           implementers.
+           Note that the player has to replace the variables between {{ and }} by its own values.
+        */
+        ajax_url: platform_url + "/ldtplatform/api/ldt/segments/{media}/{begin}/{end}", 
+        
+        ajax_granularity: 10000, /* how much ms should we look before and after the
+                                   current timecode */
+        
+        project_url: platform_url + "/ldtplatform/ldt/front/player/" /* the beginning 
+                                                                        of a link to the
+                                                                        new front */
+    } 
+  };
+};
+
+/*
+Override this if you want to change the info the player receives about the user.
+It's typically overrided in server-side templates with user-specific data.
+
+IriSP.user = {
+  "name" : "loic",
+  "avatar" : "http://a1.twimg.com/profile_images/39270812/loicempuria_normal.jpg"
+};
+*/
+
+IriSP.defaults.paths = {
+//  "imgs": "/tweetlive/res/metadataplayer/src/css/imgs"
+  "imgs": "/metadataplayer/src/css/imgs"
+};
+
+IriSP.defaults.default_templates_vars = function() { 
+  return {
+  "img_dir" : IriSP.paths.imgs 
+  };
+}
+
+/* the widget classes and definitions */
+
+/**
+  * @class Widget is an "abstract" class. It's mostly used to define some properties common to every widget.
+  *
+  *  Note that widget constructors are never called directly by the user. Instead, the widgets are instantiated by functions
+  *  defined in init.js
+  *  
+  * @constructor
+  * @param Popcorn a reference to the popcorn Object
+  * @param config configuration options for the widget
+  * @param Serializer a serializer instance from which the widget reads data fromCharCode  
+*/
+IriSP.Widget = function(Popcorn, config, Serializer) {
+
+  if (config === undefined || config === null) {
+    config = {}
+  }
+  
+  this._Popcorn = Popcorn;
+  this._config = config;  
+  this._serializer = Serializer;
+  
+  if (config.hasOwnProperty("container")) {
+     this._id = config.container;
+     this.selector = IriSP.jQuery("#" + this._id);
+  }
+
+  if (config.hasOwnProperty("spacer")) {
+     this._spacerId = config.spacer;
+     this.spacer = IriSP.jQuery("#" + this._spacerId);
+  }
+
+
+  if (config.hasOwnProperty("width")) {
+     // this.width and not this._width because we consider it public.
+     this.width = config.width;     
+  }
+  
+  if (config.hasOwnProperty("height")) {    
+     this.height = config.height;     
+  }
+  
+  if (config.hasOwnProperty("heightmax")) {
+     this.heightmax = config.heightmax;     
+  }
+
+  if (config.hasOwnProperty("widthmax")) {
+     this.widthmax = config.widthmax;     
+  } 
+
+  if (config.hasOwnProperty("layoutManager")) {
+     this.layoutManager = config.layoutManager;     
+  }
+  
+};
+
+/**
+  * This method responsible of drawing a widget on screen.
+  */
+IriSP.Widget.prototype.draw = function() {
+  /* implemented by "sub-classes" */  
+};
+
+/**
+  * Optional method if you want your widget to support redraws.
+  */
+IriSP.Widget.prototype.redraw = function() {
+  /* implemented by "sub-classes" */  
+};
+/* modules are non-graphical entities, similar to widgets */
+
+IriSP.Module = function(Popcorn, config, Serializer) {
+
+  if (config === undefined || config === null) {
+    config = {}
+  }
+  
+  this._Popcorn = Popcorn;
+  this._config = config;  
+  this._serializer = Serializer;
+};
+/* layout.js - very basic layout management */
+
+/**
+  @class a layout manager manages a div and the layout of objects
+  inside it.
+*/
+IriSP.LayoutManager = function(options) {
+    this._Popcorn = null;
+    this._widgets = [];
+    
+    this._div = "LdtPlayer";
+    this._width = 640;
+    
+    if (options === undefined) {
+      options = {};
+    };
+    
+    if (options.hasOwnProperty('container')) {
+      this._div = options.container;
+    }
+
+    if (options.hasOwnProperty('width')) {
+      this._width = options.width;
+    }    
+    
+    if (options.hasOwnProperty('height')) {
+      this._height = options.height;
+    } 
+    
+    /* this is a shortcut */
+    this.selector = IriSP.jQuery("#" + this._div);
+    
+    this.selector.css("width", this._width);
+    
+    if (this._height !== undefined)
+      this.selector.css("height", this._height);
+};
+
+/** 
+   Set the popcorn instance used by the manager.
+   
+   we need this special setter because of a chicken and egg problem :
+   we want the manager to use popcorn but the popcorn div will be managed
+   by the manager. So we need a way to set the instance the manager uses
+*/
+   
+IriSP.LayoutManager.prototype.setPopcornInstance = function(popcorn) {
+    this._Popcorn = popcorn;
+}
+
+/** create a subdiv with an unique id, and a spacer div as well.
+    @param widgetName the name of the widget.
+    @return an array of the form [createdivId, spacerdivId].
+*/
+IriSP.LayoutManager.prototype.createDiv = function(widgetName) {
+    if (typeof(widgetName) === "undefined")
+       widgetName = "";
+
+    var newDiv = IriSP.guid(this._div + "_widget_" + widgetName + "_");
+    var spacerDiv = IriSP.guid("LdtPlayer_spacer_");
+    this._widgets.push([widgetName, newDiv]);    
+
+    var divTempl = "<div id='{{id}}' style='width: {{width}}px; position: relative;'></div";
+    var spacerTempl = "<div id='{{spacer_id}}' style='width: {{width}}px; position: relative; height: {{spacer_div_height}};'></div";
+    
+    var divCode = Mustache.to_html(divTempl, {id: newDiv, width: this._width});
+    var spacerCode = Mustache.to_html(spacerTempl, {spacer_id: spacerDiv, width: this._width,
+                                                    spacer_div_height: IriSP.widgetsDefaults.LayoutManager.spacer_div_height });
+
+    this.selector.append(divCode);
+    this.selector.append(spacerCode);
+
+    return [newDiv, spacerDiv];
+};/* init.js - initialization and configuration of Popcorn and the widgets
+exemple json configuration:
+ 
+ */
+
+/**
+    set up the IriSP.__dataloader instance - 
+    we need it because we have to get the metadata
+    about the video before that the widget have even
+    loaded.
+*/
+IriSP.setupDataLoader = function() {
+  /* we set it up separately because we need to
+     get data at the very beginning, for instance when
+     setting up the video */
+  IriSP.__dataloader = new IriSP.DataLoader();
+};
+
+/** do some magic to configure popcorn according to the options object passed.
+    Works for html5, jwplayer and youtube videos 
+*/
+IriSP.configurePopcorn = function (layoutManager, options) {
+    var pop;
+    var ret = layoutManager.createDiv(); 
+    var containerDiv = ret[0];
+    var spacerDiv = ret[1];
+    
+    /* insert one pixel of margin between the video and the first widget, using the 
+       spacer.
+    */
+    IriSP.jQuery("#" + spacerDiv).css("height", "1px");
+    
+    switch(options.type) {
+      /*
+        todo : dynamically create the div/video tag which
+        will contain the video.
+      */
+      case "html5":
+           var tmpId = Popcorn.guid("video"); 
+           IriSP.jQuery("#" + containerDiv).append("<video src='" + options.file + "' id='" + tmpId + "'></video>");
+
+           if (options.hasOwnProperty("width"))
+             IriSP.jQuery("#" + containerDiv).css("width", options.width);
+           
+           if (options.hasOwnProperty("height"))
+             IriSP.jQuery("#" + containerDiv).css("height", options.height);
+
+           pop = Popcorn("#" + tmpId);
+        break;
+        
+      case "jwplayer":
+          var opts = IriSP.jQuery.extend({}, options);
+          delete opts.container;
+          delete opts.type;
+
+          
+          /* Try to guess options.file and options.streamer only if file and streamer
+             are not already defined in the configuration */
+          if (options.provider === "rtmp" && !opts.hasOwnProperty("file") && !opts.hasOwnProperty("streamer")) {
+            /* exit if we can't access the metadata */
+            if (typeof(IriSP.__jsonMetadata) === "undefined") {
+                break;
+            };
+
+            // the json format is totally illogical
+            //opts.streamer = IriSP.__jsonMetadata["medias"][0]["meta"]["item"]["value"];
+            //var source = IriSP.__jsonMetadata["medias"][0]["href"];
+
+            // the source if a full url but jwplayer wants an url relative to the
+            // streamer url, so we've got to remove the common part.
+            //opts.file = source.slice(opts.streamer.length);
+            
+            /* sometimes we get served a file with a wrong path and streamer.
+               as a streamer is of the form rtmp://domain/path/ and the media is
+               the rest, we uglily do this :
+            */
+            opts.file = "";
+            opts.streamer = "";
+            var fullPath = IriSP.__jsonMetadata["medias"][0]["href"];
+            
+            /* files can either use href or url to refer to the stream */
+            if (IriSP.null_or_undefined(fullPath)) {
+              fullPath = IriSP.__jsonMetadata["medias"][0]["url"];
+            }
+            
+            if (IriSP.null_or_undefined(fullPath)) {
+              console.log("no url or href field defined in the metadata.");
+            }
+            
+            var pathSplit = fullPath.split('/');
+            
+            for (var i = 0; i < pathSplit.length; i++) {
+              if (i < 4) {
+                 opts.streamer += pathSplit[i] + "/";
+              } else {
+                 opts.file += pathSplit[i];
+                 /* omit the last slash if we're on the last element */
+                 if (i < pathSplit.length - 1)
+                  opts.file += "/";
+              }
+            }            
+          } else {
+            /* other providers type, video for instance -
+               pass everything as is */
+          }
+
+          if (!options.hasOwnProperty("flashplayer")) {
+            opts.flashplayer = IriSP.jwplayer_swf_path;
+          }
+
+          if (!options.hasOwnProperty("controlbar.position")) {
+            opts["controlbar.position"] = "none";
+          }
+
+          pop = new IriSP.PopcornReplacement.jwplayer("#" + containerDiv, opts);
+        break;
+      
+      case "youtube":
+          var opts = IriSP.jQuery.extend({}, options);
+          delete opts.container;
+          opts.controls = 0;
+          opts.autostart = false;
+          templ = "width: {{width}}px; height: {{height}}px;";
+          var str = Mustache.to_html(templ, {width: opts.width, height: opts.height});    
+          // Popcorn.youtube wants us to specify the size of the player in the style attribute of its container div.
+          IriSP.jQuery("#" + containerDiv).attr("style", str);
+          
+          pop = Popcorn.youtube("#" + containerDiv, opts.video, opts);
+        break;
+      
+    case "dailymotion":
+        pop = new IriSP.PopcornReplacement.dailymotion("#" + containerDiv, options);
+        break;
+             
+      case "allocine":
+          /* pass the options as-is to the allocine player and let it handle everything */
+          pop = new IriSP.PopcornReplacement.allocine("#" + containerDiv, options);
+          break;
+          
+      default:
+        pop = undefined;
+    };
+    
+    return pop;
+};
+
+/** Configure the gui and instantiate the widgets passed as parameters
+    @param guiOptions the gui object as seen in the examples.
+ */
+IriSP.configureWidgets = function (popcornInstance, layoutManager, guiOptions) {
+ 
+  var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader);
+  var params = {width: guiOptions.width, height: guiOptions.height};
+
+  var default_options = guiOptions.default_options;
+  if (IriSP.null_or_undefined(default_options))
+    default_options = {};
+  
+  var ret_widgets = [];
+  var index;
+  
+  for (index = 0; index < guiOptions.widgets.length; index++) {    
+    var widget = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, guiOptions.widgets[index], default_options);
+   
+    ret_widgets.push(widget);   
+  };
+
+  return ret_widgets;
+};
+
+/** configure modules. @see configureWidgets */
+IriSP.configureModules = function (popcornInstance, modulesList) {
+  if (IriSP.null_or_undefined(modulesList))
+    return;
+  
+  var serialFactory = new IriSP.SerializerFactory(IriSP.__dataloader);
+  var ret_modules = [];
+  var index;
+  
+  for (index = 0; index < modulesList.length; index++) {    
+    var moduleConfig = modulesList[index];
+    
+    var serializer = serialFactory.getSerializer(moduleConfig.metadata);
+    var module = new IriSP[moduleConfig.type](popcornInstance, moduleConfig, serializer);    
+    ret_modules.push(module);
+  };
+
+  return ret_modules;
+};
+
+/** instantiate a widget - only called by configureWidgets, never by the user. Handles widget 
+    dependencies.
+    @param popcornInstance popcorn instance the widget will user
+    @param serialFactory serializer factory to instantiate the widget with
+    @param layoutManager layout manager
+    @param widgetConfig configuration options for the widget
+    @param defaultOptions a dictionnary with some options defined for every widget.
+ */
+IriSP.instantiateWidget = function(popcornInstance, serialFactory, layoutManager, widgetConfig, defaultOptions) {
+
+    if (IriSP.null_or_undefined(defaultOptions))
+      defaultOptions = {};
+    
+    widgetConfig = IriSP.underscore.defaults(widgetConfig, defaultOptions);
+
+    var arr = IriSP.jQuery.extend({}, widgetConfig);
+    
+    /* create a div for those widgets who didn't already specify a container; */
+    if (!arr.hasOwnProperty("container")) {
+      /* create div returns us a container for the widget and a spacer */    
+      var ret = layoutManager.createDiv(widgetConfig.type);        
+      var container = ret[0];
+      var spacer = ret[1];           
+      arr.container = container;
+      arr.spacer = spacer;
+      arr.layoutManager = layoutManager;
+    }
+    var serializer = serialFactory.getSerializer(widgetConfig.metadata);    
+    
+    if (typeof serializer == "undefined")   
+      debugger;
+    
+    // instantiate the object passed as a string
+    var widget = new IriSP[widgetConfig.type](popcornInstance, arr, serializer);    
+    
+    if (widgetConfig.hasOwnProperty("requires")) {
+      // also create the widgets this one depends on.
+      // the dependency widget is available in the parent widget context as
+      // this.WidgetName (for instance, this.TipWidget);
+      
+      var i = 0;
+      for(i = 0; i < widgetConfig.requires.length; i++) {
+        var widgetName = widgetConfig.requires[i]["type"];
+        widget[widgetName] = IriSP.instantiateWidget(popcornInstance, serialFactory, layoutManager, widgetConfig.requires[i], defaultOptions);
+      }
+    }       
+     
+    serializer.sync(IriSP.wrap(widget, function() { this.draw(); }));
+    return widget;
+};
+
+/** Go through the defaults to set a reasonable value */
+IriSP.configureDefaults = function(libdir, platform_url) {
+  /* the defaults configuration is messy and complicated. There are two things to know :
+     - we want to allow overwriting of defaults - that's why we have IriSP.widgetDefaults
+       and IriSP.defaults.widgetDefaults. The first is filled by the embedder and then fleshed out
+       with the contents of the first. We use underscore.defaults for that, but there's one problem with
+       this function : it doesn't work recursively.
+     - we need to compute some values at runtime instead of at compile time
+  */
+    
+  IriSP.lib = IriSP.underscore.defaults(IriSP.lib, IriSP.defaults.lib(libdir));
+  
+  /* get the factory defaults for the widgets and merge them with the default the user
+     may have defined 
+  */
+  var factory_defaults = IriSP.defaults.widgetsDefaults(platform_url);
+  for(var widget in factory_defaults) {
+  
+      /* create the object if it doesn't exists */
+      if (IriSP.null_or_undefined(IriSP.widgetsDefaults[widget]))
+        IriSP.widgetsDefaults[widget] = {};
+        
+      IriSP.widgetsDefaults[widget] = IriSP.underscore.defaults(IriSP.widgetsDefaults[widget], factory_defaults[widget]);
+  }
+  
+  IriSP.paths = IriSP.underscore.defaults(IriSP.paths, IriSP.defaults.paths);
+  IriSP.default_templates_vars = IriSP.underscore.defaults(IriSP.default_templates_vars, 
+                                       IriSP.defaults.default_templates_vars());
+
+  if (IriSP.null_or_undefined(IriSP.user))
+    IriSP.user = {};
+  
+  IriSP.user = IriSP.underscore.defaults(IriSP.user, IriSP.defaults.user());
+};
+
+/** single point of entry for the metadataplayer */
+IriSP.initPlayer = function(config, metadata_url, libdir, platform_url) {
+    IriSP.configureDefaults(libdir, platform_url);
+    IriSP.loadLibs(IriSP.lib, config, metadata_url,
+      function() {   
+              
+              var layoutManager = new IriSP.LayoutManager(config.gui);
+
+              var pop = IriSP.configurePopcorn(layoutManager, config.player);
+              
+              var widgets = IriSP.configureWidgets(pop, layoutManager, config.gui); 
+              var modules = IriSP.configureModules(pop, config.modules); 
+      });
+};/* To wrap a player the develop should create a new class derived from
+the IriSP.PopcornReplacement.player and defining the correct functions */
+
+/** allocine player wrapper */
+IriSP.PopcornReplacement.allocine = function(container, options) {
+    console.log("Calling allocine player");
+    /* appel du parent pour initialiser les structures communes à tous les players */
+    IriSP.PopcornReplacement.player.call(this, container, options);   
+    
+    var _this = this;
+
+    /* Définition des fonctions de l'API -  */
+
+    this.playerFns = {
+        play : function() {
+            if (_this.player) {
+                console.log("ask play _this.player = " + _this.player);
+                return _this.player.sendToActionScript("play");
+            } else {
+                return false;
+            }
+        },
+        pause : function() {
+            if (_this.player) {
+                console.log("ask pause _this.player = " + _this.player);
+                return _this.player.sendToActionScript("pause");
+            } else {
+                return false;
+            }
+        },
+        getPosition : function() {
+            if (_this.player) {
+                return _this.player.sendToActionScript("getSeek","return");
+            } else {
+                return 0;
+            }
+        },
+        seek : function(pos) {
+            if (_this.player) {
+                return _this.player.sendToActionScript("seek",pos);
+            } else {
+                return false;
+            }
+        },
+        getMute : function() {
+            if (_this.player) {
+                return _this.player.sendToActionScript("getMute","return");
+            } else {
+                return false;
+            }
+        },
+        setMute : function(p) {
+            if (_this.player) {
+                //return p ? _this.player.sendToActionScript("setMute") : _this.player.sendToActionScript("setMute");
+            	_this.player.sendToActionScript("setMute");
+            } else {
+                return false;
+            }
+        }
+    }
+
+    window.onReady = IriSP.wrap(this, this.ready);
+    //NOT CALLED window.onAllocineStateChange = IriSP.wrap(this, this.stateHandler);
+    window.onTime = IriSP.wrap(this, this.progressHandler);
+    
+    var fv = "adVast=false&lg=fr_cinecast&autoPlay="+options.autoPlay+"&directVideoTitle= &urlAcData="+options.urlAcData+"&directVideoPath="+options.directVideoPath+"&host=http://allocine.fr";
+    console.log("fv = " + fv);
+    
+    var params = {
+        "allowScriptAccess" : "always",
+        "wmode": "opaque",
+        "flashvars" : fv
+    };
+    var atts = {
+        id : this.container
+    };
+    swfobject.embedSWF(options.acPlayerUrl, this.container, options.width, options.height, "8", null, null, params, atts);
+
+};
+
+IriSP.PopcornReplacement.allocine.prototype = new IriSP.PopcornReplacement.player("", {});
+
+IriSP.PopcornReplacement.allocine.prototype.ready = function() {
+    this.player = document.getElementById(this.container);
+    this.player.addEventListener("onStateChange", "onAllocineStateChange");
+    this.player.addEventListener("onVideoProgress", "onAllocineVideoProgress");
+    this.player.cueVideoByUrl(this._options.video);
+    this.callbacks.onReady();
+};
+
+IriSP.PopcornReplacement.allocine.prototype.progressHandler = function(progressInfo) {
+    this.callbacks.onTime({
+        position: progressInfo.mediaTime
+    });
+}
+
+
+IriSP.PopcornReplacement.allocine.prototype.stateHandler = function(state) {
+    
+    switch(state) {
+        case 1:
+            this.callbacks.onPlay();
+            break;
+
+        case 2:
+            this.callbacks.onPause();
+            break;
+
+        case 3:
+            this.callbacks.onSeek({
+                position: this.player.getCurrentTime()
+            });
+            break;
+
+        /*
+        case 5:
+            this.callbacks.onReady();
+            break;
+        */
+    }
+    
+};/* To wrap a player the develop should create a new class derived from
+the IriSP.PopcornReplacement.player and defining the correct functions */
+
+/** jwplayer player wrapper */
+IriSP.PopcornReplacement.dailymotion = function(container, options) {
+    console.log("Calling");
+    /* appel du parent pour initialiser les structures communes à tous les players */
+    IriSP.PopcornReplacement.player.call(this, container, options);   
+    
+    var _this = this;
+
+    /* Définition des fonctions de l'API -  */
+
+    this.playerFns = {
+        play : function() {
+            if (_this.player) {
+                return _this.player.playVideo();
+            } else {
+                return false;
+            }
+        },
+        pause : function() {
+            if (_this.player) {
+                return _this.player.pauseVideo();
+            } else {
+                return false;
+            }
+        },
+        getPosition : function() {
+            if (_this.player) {
+                return _this.player.getCurrentTime();
+            } else {
+                return 0;
+            }
+        },
+        seek : function(pos) {
+            if (_this.player) {
+                return _this.player.seekTo(pos);
+            } else {
+                return false;
+            }
+        },
+        getMute : function() {
+            if (_this.player) {
+                return _this.player.isMuted();
+            } else {
+                return false;
+            }
+        },
+        setMute : function(p) {
+            if (_this.player) {
+                return p ? _this.player.mute() : _this.player.unMute();
+            } else {
+                return false;
+            }
+        }
+    }
+
+    window.onDailymotionPlayerReady = IriSP.wrap(this, this.ready);
+    window.onDailymotionStateChange = IriSP.wrap(this, this.stateHandler);
+    window.onDailymotionVideoProgress = IriSP.wrap(this, this.progressHandler);
+
+    var params = {
+        "allowScriptAccess" : "always",
+        "wmode": "opaque"
+    };
+    var atts = {
+        id : this.container
+    };
+    swfobject.embedSWF("http://www.dailymotion.com/swf?chromeless=1&enableApi=1", this.container, options.width, options.height, "8", null, null, params, atts);
+
+};
+
+IriSP.PopcornReplacement.dailymotion.prototype = new IriSP.PopcornReplacement.player("", {});
+
+IriSP.PopcornReplacement.dailymotion.prototype.ready = function() {
+    
+    this.player = document.getElementById(this.container);
+    
+    this.player.addEventListener("onStateChange", "onDailymotionStateChange");
+    this.player.addEventListener("onVideoProgress", "onDailymotionVideoProgress");
+    this.player.cueVideoByUrl(this._options.video);
+    
+    this.callbacks.onReady();
+};
+
+IriSP.PopcornReplacement.dailymotion.prototype.progressHandler = function(progressInfo) {
+    
+    this.callbacks.onTime({
+        position: progressInfo.mediaTime
+    });
+}
+
+IriSP.PopcornReplacement.dailymotion.prototype.stateHandler = function(state) {
+    
+    switch(state) {
+        case 1:
+            this.callbacks.onPlay();
+            break;
+
+        case 2:
+            this.callbacks.onPause();
+            break;
+
+        case 3:
+            this.callbacks.onSeek({
+                position: this.player.getCurrentTime()
+            });
+            break;
+
+        /*
+        case 5:
+            this.callbacks.onReady();
+            break;
+        */
+    }
+    
+};/* To wrap a player the develop should create a new class derived from 
+   the IriSP.PopcornReplacement.player and defining the correct functions */
+
+/** jwplayer player wrapper */
+IriSP.PopcornReplacement.jwplayer = function(container, options) {
+
+  /* appel du parent pour initialiser les structures communes à tous les players */
+  IriSP.PopcornReplacement.player.call(this, container, options);
+  
+  this.media.duration = options.duration; /* optional */
+  
+  /* Définition des fonctions de l'API -  */
+  this.playerFns = {
+    play: function() { return jwplayer(this.container).play(); },
+    pause: function() { return jwplayer(this.container).pause(); },
+    getPosition: function() { return jwplayer(this.container).getPosition(); },
+    seek: function(pos) { return jwplayer(this.container).seek(pos); },
+    getMute: function() { return jwplayer(this.container).getMute() },
+    setMute: function(p) { return jwplayer(this.container).setMute(p); }
+  }
+
+  options.events = this.callbacks;
+
+  jwplayer(this.container).setup(options);
+};
+
+IriSP.PopcornReplacement.jwplayer.prototype = new IriSP.PopcornReplacement.player("", {});
+/* embed module - listens and relay hash changes to a parent window. */
+
+IriSP.EmbedModule = function(Popcorn, config, Serializer) {
+  IriSP.Module.call(this, Popcorn, config, Serializer);
+
+  window.addEventListener('message', IriSP.wrap(this, this.handleMessages), false);
+  this._Popcorn.listen("IriSP.Mediafragment.hashchange", IriSP.wrap(this, this.relayChanges));
+};
+
+IriSP.EmbedModule.prototype = new IriSP.Module();
+
+IriSP.EmbedModule.prototype.handleMessages = function(e) {
+  if (e.data.type === "hashchange") {
+    window.location.hash = e.data.value;    
+  }  
+};
+
+IriSP.EmbedModule.prototype.relayChanges = function(newHash) {
+  window.parent.postMessage({type: "hashchange", value: newHash}, "*"); 
+  return;
+};/* mediafragment module */
+
+IriSP.MediaFragment = function(Popcorn, config, Serializer) {
+  IriSP.Module.call(this, Popcorn, config, Serializer);
+
+  this.mutex = false; /* a mutex because we access the url from two different functions */
+
+  this._Popcorn.listen( "loadedmetadata", IriSP.wrap(this,this.advanceTime));
+  this._Popcorn.listen( "pause", IriSP.wrap(this,this.updateTime));
+  this._Popcorn.listen( "seeked", IriSP.wrap(this,this.updateTime));
+  this._Popcorn.listen( "IriSP.PolemicTweet.click", IriSP.wrap(this,this.updateAnnotation));
+  this._Popcorn.listen( "IriSP.SegmentsWidget.click", IriSP.wrap(this,this.updateAnnotation));
+  
+  window.onhashchange = IriSP.wrap(this, this.advanceTime);
+};
+
+IriSP.MediaFragment.prototype = new IriSP.Module();
+
+IriSP.MediaFragment.prototype.advanceTime = function() {
+             var url = window.location.href;
+
+              if ( url.split( "#" )[ 1 ] != null ) {
+                  pageoffset = url.split( "#" )[1];
+
+                  if ( pageoffset.substring(0, 2) === "t=") {
+                    // timecode 
+                    if ( pageoffset.substring( 2 ) != null ) {
+                    var offsettime = pageoffset.substring( 2 );
+                    this._Popcorn.currentTime( parseFloat(offsettime) );
+                    
+                    /* we have to trigger this signal manually because of a
+                     bug in the jwplayer */
+                    this._Popcorn.trigger("seeked", parseFloat(offsettime));
+                    }
+                  } else if ( pageoffset.substring(0, 3) === "id=") {
+                    // annotation
+                    var annotationId = pageoffset.substring( 3 );
+                    // there's no better way than that because
+                    // of possible race conditions
+                    this._serializer.sync(IriSP.wrap(this, function() {
+                          this.lookupAnnotation.call(this, annotationId); 
+                          }));
+                  }
+              }
+};
+
+/** handler for the seeked signal. It may have or may have not an argument.
+    @param time if not undefined, the time we're seeking to 
+*/
+IriSP.MediaFragment.prototype.updateTime = function(time) {
+  if (this.mutex === true) {
+    return;
+  }
+
+  var history = window.history;
+  if ( !history.pushState ) {
+    return false;
+  }
+    
+  if (IriSP.null_or_undefined(time) || typeof(time) != "number") {
+    var ntime = this._Popcorn.currentTime().toFixed(2)
+  } else {
+    var ntime = time.toFixed(2);
+  }
+
+  // used to relay the new hash to the embedder
+  this._Popcorn.trigger("IriSP.Mediafragment.hashchange", "#t=" + ntime);
+  
+  splitArr = window.location.href.split( "#" )
+  history.replaceState( {}, "", splitArr[0] + "#t=" + ntime );
+};
+
+
+IriSP.MediaFragment.prototype.updateAnnotation = function(annotationId) {
+  var _this = this;
+  this.mutex = true;
+
+  var history = window.history;
+  if ( !history.pushState ) {
+    return false;
+  }
+ 
+  
+  // used to relay the new hash to the embedder
+  this._Popcorn.trigger("IriSP.Mediafragment.hashchange", "#id=" + annotationId);
+  
+  splitArr = window.location.href.split( "#" )
+  history.replaceState( {}, "", splitArr[0] + "#id=" + annotationId);
+
+  
+  // reset the mutex afterwards to prevent the module from reacting to his own changes.
+  window.setTimeout(function() { _this.mutex = false }, 50);
+};
+
+// lookup and seek to the beginning of an annotation
+IriSP.MediaFragment.prototype.lookupAnnotation = function(annotationId) {
+  var _this = this;
+  this.mutex = true;
+
+  var annotation = undefined;
+  var annotations = this._serializer._data.annotations;
+
+  var i;
+  for (i = 0; i < annotations.length; i++) {
+      if (annotations[i].id === annotationId) {
+        annotation = annotations[i];
+        break;
+      }
+  }
+
+  if (typeof(annotation) !== "undefined") {
+    this._Popcorn.currentTime(annotation.begin / 1000);
+
+    /* we have to trigger this signal manually because of a
+     bug in the jwplayer */
+    this._Popcorn.trigger("seeked", annotation.begin / 1000);
+    this._Popcorn.trigger("IriSP.Mediafragment.showAnnotation", annotationId);
+  }
+  
+  window.setTimeout(function() { _this.mutex = false }, 50);
+};
+IriSP.AnnotationsListWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  this.__counter = 0;
+  this.__oldList = [];
+  
+  this.ajax_mode = IriSP.widgetsDefaults["AnnotationsListWidget"].ajax_mode;
+  this.project_url = IriSP.widgetsDefaults["AnnotationsListWidget"].project_url;  
+};
+
+
+IriSP.AnnotationsListWidget.prototype = new IriSP.Widget();
+
+IriSP.AnnotationsListWidget.prototype.clear = function() {
+};
+
+IriSP.AnnotationsListWidget.prototype.clearWidget = function() {
+};
+
+/** effectively redraw the widget - called by drawList */
+IriSP.AnnotationsListWidget.prototype.do_redraw = function(list) {
+    var widgetMarkup = IriSP.templToHTML(IriSP.annotationsListWidget_template, {annotations: list});
+    this.selector.html(widgetMarkup);
+};
+
+/** draw the annotation list */
+IriSP.AnnotationsListWidget.prototype.drawList = function(force_redraw) {
+  var _this = this;
+  
+  var view_type = this._serializer.getContributions();
+  var annotations = this._serializer._data.annotations;
+  var currentTime = this._Popcorn.currentTime();
+    
+  var list = [];
+
+  if (typeof(view_type) === "undefined") {    
+    return;
+  }
+
+  for (i = 0; i < annotations.length; i++) {
+    var annotation = annotations[i];
+
+    /* filter the annotations whose type is not the one we want */
+    if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
+          && annotation.meta["id-ref"] !== view_type) {
+        continue;
+    }
+
+    /* only get the annotations happening in the current chapter */
+    if (!(annotation.begin <= currentTime * 1000 && annotation.end > currentTime * 1000)) {        
+        continue;
+    }
+
+    var a = annotation;
+    var obj = {};
+
+    obj["id"] = a.id;
+    obj["title"] = a.content.title;
+    obj["desc"] = a.content.description;
+    obj["begin"] = IriSP.msToTime(annotation.begin);
+    obj["end"] = IriSP.msToTime(annotation.end);
+
+    list.push(obj);
+  }
+  
+  var idList = IriSP.underscore.pluck(list, "id").sort();
+
+  if (idList.length !== this.__oldList.length) {
+    this.do_redraw(list);
+  }
+    
+  var res = 1;
+  for (var i = 0; i < idList.length; i++) {
+    if (idList[i] !== this.__oldList[i])
+      res = 0;
+      break;
+  }
+  
+  this.__oldList = idList; /* save for next call */
+  
+  if (typeof(force_redraw) !== "undefined") {
+    this.do_redraw(list);
+  }
+  
+  /* the two lists are equal, no need to redraw */
+  if (res === 1) {
+    return;
+  } else {
+    this.do_redraw(list);
+  }
+  
+};
+
+IriSP.AnnotationsListWidget.prototype.ajaxRedraw = function(timecode) {
+
+  /* the seeked signal sometimes passes an argument - depending on if we're using
+     our popcorn lookalike or the real thing - if it's the case, use it as it's
+     more precise than currentTime which sometimes contains the place we where at */
+  if (IriSP.null_or_undefined(timecode) || typeof(timecode) != "number") {
+     var tcode = this._Popcorn.currentTime();     
+   } else {
+     var tcode = timecode;     
+  }
+   
+  
+  /* the platform gives us a special url - of the type : http://path/{media}/{begin}/{end}
+     we double the braces using regexps and we feed it to mustache to build the correct url
+     we have to do that because the platform only knows at run time what view it's displaying.
+  */
+     
+  var platf_url = IriSP.widgetsDefaults["AnnotationsListWidget"].ajax_url
+                                      .replace(/\{/g, '{{').replace(/\}/g, '}}');
+  var media_id = this._serializer.currentMedia()["id"];
+  var duration = +this._serializer.currentMedia().meta["dc:duration"];
+  
+  var begin_timecode = (Math.floor(tcode) - 300) * 1000;
+  if (begin_timecode < 0)
+    begin_timecode = 0;
+    
+  var end_timecode = (Math.floor(tcode) + 300) * 1000;
+  if (end_timecode > duration)
+    end_timecode = duration;
+  
+  var templ = Mustache.to_html(platf_url, {media: media_id, begin: begin_timecode,
+                                 end: end_timecode});
+
+  /* we create on the fly a serializer to get the ajax */
+  var serializer = new IriSP.JSONSerializer(IriSP.__dataloader, templ);
+  serializer.sync(IriSP.wrap(this, function(json) { this.processJson(json, serializer) }));                  
+};
+
+/** process the received json - it's a bit hackish */
+IriSP.AnnotationsListWidget.prototype.processJson = function(json, serializer) {
+  /* FIXME: DRY the whole thing */
+  var annotations = serializer._data.annotations;
+  if (IriSP.null_or_undefined(annotations))
+    return;
+  
+  /*
+  commented in case we wanted to discriminate against some annotation types.
+  var view_types = serializer.getIds("Contributions");
+  */
+  var l = [];
+  
+  var media = this._serializer.currentMedia()["id"];
+  
+  for (i = 0; i < annotations.length; i++) {
+      var annotation = annotations[i];
+
+      /* filter the annotations whose type is not the one we want */
+      /* We want _all_ the annotations.
+      if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
+            && !IriSP.underscore.include(view_types, annotation.meta["id-ref"])) {
+          continue;
+      }
+      */
+      var a = annotation;
+      var obj = {};
+
+      obj["id"] = a.id;
+      obj["title"] = a.content.title;
+      obj["desc"] = a.content.description;
+      obj["begin"] = IriSP.msToTime(annotation.begin);
+      obj["end"] = IriSP.msToTime(annotation.end);
+
+      /* only if the annotation isn't present in the document create an
+         external link */
+      if (!this.annotations_ids.hasOwnProperty(obj["id"])) {
+        // braindead url; jacques didn't want to create a new one in the platform,
+        // so we append the cutting id to the url.
+        obj["url"] = this.project_url + "/" + media + "/" + 
+                     annotation.meta["project"] + "/" +
+                     annotation.meta["id-ref"];
+        
+        // obj["url"] = document.location.href.split("#")[0] + "/" + annotation.meta["project"];
+      }
+      
+      l.push(obj);
+  }
+
+  this.do_redraw(l);
+};
+IriSP.AnnotationsListWidget.prototype.draw = function() {
+  
+  /* build a table of the annotations present in the document for faster 
+     lookup
+  */
+  this.annotations_ids = {};
+  
+  var annotations = this._serializer._data.annotations;
+  var i = 0;
+  for(i = 0; i < annotations.length; i++) {
+    this.annotations_ids[annotations[i]["id"]] = 1;
+  }
+  
+  this.drawList();
+    
+  if (!this.ajax_mode) {    
+    this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, function() { this.drawList(true); }));
+    this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.redraw));
+  } else {
+    /* update the widget when the video has finished loading and when it's seeked and paused */
+    this._Popcorn.listen("seeked", IriSP.wrap(this, this.ajaxRedraw));
+    this._Popcorn.listen("loadedmetadata", IriSP.wrap(this, this.ajaxRedraw));
+    this._Popcorn.listen("paused", IriSP.wrap(this, this.ajaxRedraw));
+    
+    this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, this.ajaxRedraw));
+  }
+
+};
+
+IriSP.AnnotationsListWidget.prototype.redraw = function() {
+  this.drawList();
+};IriSP.AnnotationsWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  /* flag used when we're creating an annotation */
+  this._hidden = false;
+};
+
+
+IriSP.AnnotationsWidget.prototype = new IriSP.Widget();
+
+IriSP.AnnotationsWidget.prototype.clear = function() {
+    this.selector.find(".Ldt-SaTitle").text("");
+    this.selector.find(".Ldt-SaDescription").text("");
+    this.selector.find(".Ldt-SaKeywordText").text("");
+};
+
+IriSP.AnnotationsWidget.prototype.displayAnnotation = function(annotation) {       
+    var title = annotation.content.title;
+    var description = annotation.content.description;
+    var keywords =  "";
+    var begin = +annotation.begin / 1000;
+    var end = +annotation.end / 1000;
+    var duration = +this._serializer.currentMedia().meta["dc:duration"];
+    var tags = "";
+    
+    var title_templ = "{{title}} - ( {{begin}} - {{end}} )";
+    var endstr = Mustache.to_html(title_templ, {title: title, begin: IriSP.secondsToTime(begin), end: IriSP.secondsToTime(end)});
+
+    this.selector.find(".Ldt-SaTitle").text(endstr);
+    this.selector.find(".Ldt-SaDescription").text(description);
+    
+    
+    if (!IriSP.null_or_undefined(annotation.tags) && !IriSP.null_or_undefined(this._serializer._data.tags)) {
+      /* save the tag id and keywords in a unique structure */
+      var tag_list = {};
+      for (var i = 0; i < this._serializer._data.tags.length; i++) {
+        var id = this._serializer._data.tags[i]["id"];
+        var keyword = this._serializer._data.tags[i]["meta"]["dc:title"];
+
+        tag_list[id] = keyword;
+      }
+
+      /* then browse the list of defined tags for the current annotation */
+      for (var i = 0; i < annotation.tags.length; i++) {
+        if (tag_list.hasOwnProperty(annotation.tags[i]["id-ref"]))
+          tags += tag_list[annotation.tags[i]["id-ref"]] + ", ";
+      }
+    }
+    
+    tags = "Keywords: " + tags.slice(0, tags.length - 2);
+    
+    this.selector.find(".Ldt-SaKeywords").text(tags);
+    
+    // update sharing buttons
+    var defaults = IriSP.widgetsDefaults.AnnotationsWidget;
+    var text = defaults.share_text;
+    var fb_link = defaults.fb_link;
+    var tw_link = defaults.tw_link;
+    var gplus_link = defaults.gplus_link;
+    var url = document.location.href + "#id=" + annotation.id;
+    this.selector.find(".Ldt-fbShare").attr("href", IriSP.mkFbUrl(url, text));
+    this.selector.find(".Ldt-TwShare").attr("href", IriSP.mkTweetUrl(url, text));
+    this.selector.find(".Ldt-GplusShare").attr("href", IriSP.mkGplusUrl(url, text));
+};
+
+IriSP.AnnotationsWidget.prototype.clearWidget = function() {   
+    /* retract the pane between two annotations */
+    this.selector.find(".Ldt-SaTitle").text("");
+    this.selector.find(".Ldt-SaDescription").text("");
+    this.selector.find(".Ldt-SaKeywordText").html("");
+    this.selector.find(".Ldt-ShowAnnotation").slideUp();
+};
+
+IriSP.AnnotationsWidget.prototype.draw = function() {
+  var _this = this;
+
+  var annotationMarkup = IriSP.templToHTML(IriSP.annotationWidget_template);
+	this.selector.append(annotationMarkup);
+
+  this._Popcorn.listen("IriSP.AnnotationsWidget.show", 
+                        IriSP.wrap(this, this.show));
+  this._Popcorn.listen("IriSP.AnnotationsWidget.hide", 
+                        IriSP.wrap(this, this.hide));
+ 
+  var legal_ids = [];
+  if (typeof(this._serializer.getChapitrage()) !== "undefined")
+    legal_ids.push(this._serializer.getChapitrage());
+  else 
+    legal_ids = this._serializer.getNonTweetIds();
+  
+  var annotations = this._serializer._data.annotations;
+  var i;
+  
+	for (i in annotations) {    
+    var annotation = annotations[i];
+    var begin = Math.round((+ annotation.begin) / 1000);
+    var end = Math.round((+ annotation.end) / 1000);
+
+    if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
+          && !IriSP.underscore.include(legal_ids, annotation.meta["id-ref"])) {
+        continue;
+    }
+
+
+    var conf = {start: begin, end: end, 
+                onStart: 
+                       function(annotation) { 
+                        return function() { 
+                            _this.displayAnnotation(annotation); 
+                          
+                        } }(annotation),
+                onEnd: 
+                       function() { _this.clearWidget.call(_this); }
+                };
+    this._Popcorn = this._Popcorn.code(conf);                                             
+  }
+
+};
+
+IriSP.AnnotationsWidget.prototype.hide = function() {
+  if (this._hidden == false) {
+    this.selector.hide();
+    this._hidden = true;
+  }
+};
+
+IriSP.AnnotationsWidget.prototype.show = function() {
+  if (this._hidden == true) {
+    this.selector.show();
+    this._hidden = false;
+  }
+};IriSP.ArrowWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+
+  this._oldAnnotation = null;
+  this._blockArrow = false;
+};
+
+
+IriSP.ArrowWidget.prototype = new IriSP.Widget();
+
+IriSP.ArrowWidget.prototype.clear = function() {
+
+};
+
+IriSP.ArrowWidget.prototype.clearWidget = function() {
+};
+
+IriSP.ArrowWidget.prototype.draw = function() {
+  var templ = Mustache.to_html(IriSP.arrowWidget_template, {});
+  this.selector.append(templ);
+  this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler));
+  this._Popcorn.listen("IriSP.ArrowWidget.blockArrow", IriSP.wrap(this, this.blockArrow));
+  this._Popcorn.listen("IriSP.ArrowWidget.releaseArrow", IriSP.wrap(this, this.releaseArrow));
+  
+};
+
+IriSP.ArrowWidget.prototype.timeUpdateHandler = function(percents) {
+  if (this._blockArrow)
+    return;
+  
+  var currentTime = this._Popcorn.currentTime();
+  var currentAnnotation = this._serializer.currentChapitre(currentTime);
+  if (IriSP.null_or_undefined(currentAnnotation)) {
+    var c_annots = this._serializer.currentAnnotation(currentTime)
+    if (c_annots.length != 0)
+      var currentAnnotation = c_annots[0]; // FIXME : use the others ?
+    else
+      return;
+  }
+  
+  /* move the arrow only if the current annotation changes */
+  if (currentAnnotation != this._oldAnnotation) {
+    var begin = (+ currentAnnotation.begin) / 1000;
+    var end = (+ currentAnnotation.end) / 1000;
+
+    var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000;
+    var middle_time = (begin + end) / 2;
+    var percents = middle_time / duration;
+
+    // we need to apply a fix because the arrow has a certain length
+    // it's half the length of the arrow (27 / 2). We need to convert
+    // it in percents though.
+    var totalWidth = this.selector.width();    
+    var pixels = percents * totalWidth;
+    var correction = (27 / 2);
+    var corrected_pixels = pixels - correction;
+    
+    /* make sure that the arrow is aligned with the pattern
+       of the widget under it */
+    if (corrected_pixels % 3 != 0)
+      corrected_pixels -= (corrected_pixels % 3 - 1);
+    
+    /* don't move out of the screen */
+    if (corrected_pixels <= 0)
+      corrected_pixels = 0;
+    
+    if (corrected_pixels <= 15) {      
+      this.selector.children(".Ldt-arrowWidget").removeClass("Ldt-arrowLeftEdge Ldt-arrowCenter Ldt-arrowRightEdge")
+                                                .addClass("Ldt-arrowLeftEdge"); 
+    } else if (corrected_pixels >= totalWidth - 25) {
+           this.selector.children(".Ldt-arrowWidget").removeClass("Ldt-arrowLeftEdge Ldt-arrowCenter Ldt-arrowRightEdge")
+                                                .addClass("Ldt-arrowRightEdge"); 
+    } else {
+      this.selector.children(".Ldt-arrowWidget").removeClass("Ldt-arrowLeftEdge Ldt-arrowCenter Ldt-arrowRightEdge")
+                                                .addClass("Ldt-arrowCenter"); 
+    }
+    
+    this.selector.children(".Ldt-arrowWidget").animate({"left" : corrected_pixels + "px"});
+
+    this._oldAnnotation = currentAnnotation;
+  }
+};
+
+/** Block the arrow for instance when the user is annotating */
+IriSP.ArrowWidget.prototype.blockArrow = function() {
+  this._blockArrow = true;
+};
+
+IriSP.ArrowWidget.prototype.releaseArrow = function() {
+  this._blockArrow = false;   
+};
+IriSP.createAnnotationWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  this._hidden = true;
+  this.keywords = IriSP.widgetsDefaults["createAnnotationWidget"].keywords;
+  
+  this.polemic_mode = IriSP.widgetsDefaults["createAnnotationWidget"].polemic_mode;
+  this.polemics = IriSP.widgetsDefaults["createAnnotationWidget"].polemics;
+  
+  this.cinecast_version = IriSP.widgetsDefaults["createAnnotationWidget"].cinecast_version;
+  this.api_endpoint_template = IriSP.widgetsDefaults["createAnnotationWidget"].api_endpoint_template;
+  
+  this.ids = {}; /* a dictionnary linking buttons ids to keywords */
+  
+  /* variables to save the current position of the slicer */
+  if (this.cinecast_version) {
+    this.sliceLeft = 0;
+    this.sliceWidth = 0;
+  }
+};
+
+
+IriSP.createAnnotationWidget.prototype = new IriSP.Widget();
+
+IriSP.createAnnotationWidget.prototype.clear = function() {
+    this.selector.find(".Ldt-SaTitle").text("");
+    this.selector.find(".Ldt-SaDescription").text("");
+    this.selector.find(".Ldt-SaKeywordText").text("");
+};
+
+IriSP.createAnnotationWidget.prototype.draw = function() {
+  var _this = this;
+  var template_params = {cinecast_version: this.cinecast_version, 
+                         polemic_mode: this.polemic_mode};
+                         
+  if (!IriSP.null_or_undefined(IriSP.user) && !IriSP.null_or_undefined(IriSP.user.avatar))
+    template_params["user_avatar"] = IriSP.user.avatar;
+  
+  var annotationMarkup = IriSP.templToHTML(IriSP.createAnnotationWidget_template, 
+                                           template_params);
+  
+	this.selector.append(annotationMarkup);
+  
+  if (!this.cinecast_version)
+    this.selector.hide();
+  else {
+    this.showStartScreen();
+  }
+  
+  // add the keywords.
+  for (var i = 0; i < this.keywords.length; i++) {
+    var keyword = this.keywords[i];
+    var id = IriSP.guid("button_");
+    var templ = IriSP.templToHTML("<button id={{id}} class='Ldt-createAnnotation-absent-keyword'>{{keyword}}</button>", 
+                                  {keyword: keyword, id: id});
+                                  
+    this.ids[keyword] = id; // save it for the function that handle textarea changes.
+    
+    this.selector.find(".Ldt-createAnnotation-keywords").append(templ);
+    this.selector.find("#" + id).click(function(keyword) { return function() {
+      var contents = _this.selector.find(".Ldt-createAnnotation-Description").val();
+      if (contents.indexOf(keyword) != -1) {
+        var newVal = contents.replace(" " + keyword, "");
+        if (newVal == contents)
+          newVal = contents.replace(keyword, "");
+      } else {
+        if (contents === "")
+          var newVal = keyword;
+        else
+          var newVal = contents + " " + keyword;      
+      }
+      
+      _this.selector.find(".Ldt-createAnnotation-Description").val(newVal);
+      // we use a custom event because there's no simple way to test for a js
+      // change in a textfield.
+      _this.selector.find(".Ldt-createAnnotation-Description").trigger("js_mod");
+      // also call our update function.
+      //_this.handleTextChanges();
+    }
+   }(keyword));
+  }
+
+  // add the polemic buttons.
+  if(this.polemic_mode)
+    for (var polemic in this.polemics) {
+
+      var classname = IriSP.templToHTML("Ldt-createAnnotation-polemic-{{classname}}", {classname : this.polemics[polemic]});
+
+      var templ = IriSP.templToHTML("<button class='{{classname}} Ldt-createAnnotation-polemic-button'>{{polemic}}</button>",
+                  {classname: classname, polemic: polemic});
+                  
+      this.selector.find(".Ldt-createAnnotation-polemics").append(templ);
+      this.selector.find("." + classname).click(function(polemic) { return function() {
+          var contents = _this.selector.find(".Ldt-createAnnotation-Description").val();
+          if (contents.indexOf(polemic) != -1) {
+            var newVal = contents.replace(" " + polemic, "");
+            if (newVal == contents)
+              newVal = contents.replace(polemic, "");
+          } else {
+            if (contents === "")
+              var newVal = polemic;
+            else
+              var newVal = contents + " " + polemic;      
+          }
+          
+          _this.selector.find(".Ldt-createAnnotation-Description").val(newVal);
+          
+          // also call our update function.
+          _this.handleTextChanges();
+        }
+       }(polemic));
+      }    
+  
+  // js_mod is a custom event because there's no simple way to test for a js
+  // change in a textfield.                    
+  this.selector.find(".Ldt-createAnnotation-Description")
+               .bind("propertychange keyup input paste js_mod", IriSP.wrap(this, this.handleTextChanges));
+               
+  /* the cinecast version of the player is supposed to pause when the user clicks on the button */
+  if (this.cinecast_version) {
+    this.selector.find(".Ldt-createAnnotation-Description")
+                 .one("propertychange keyup input paste js_mod", 
+                 function() { if (!_this._Popcorn.media.paused) _this._Popcorn.pause() });
+  }
+  /* the cinecast version expects the user to comment on a defined segment.
+     As the widget is always shown, we need a way to update it's content as
+     time passes. We do this like we did with the annotationsWidget : we schedule
+     a .code start function which will be called at the right time.
+  */
+  if (this.cinecast_version) {
+    var legal_ids;
+    if (typeof(this._serializer.getChapitrage()) !== "undefined")
+      legal_id = this._serializer.getChapitrage();
+    else 
+      legal_id = this._serializer.getNonTweetIds()[0];
+    
+    var annotations = this._serializer._data.annotations;
+    var i;
+  
+    for (i in annotations) {     
+      var annotation = annotations[i];
+      if (typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
+            && legal_id !== annotation.meta["id-ref"]) {
+          continue;
+      }
+      
+      code = {start: annotation.begin / 1000, end: annotation.end / 1000,
+              onStart: function(annotation) { return function() {
+                      if (typeof(annotation.content) !== "undefined")
+                        _this.selector.find(".Ldt-createAnnotation-Title").html(annotation.content.title);
+
+                      _this._currentAnnotation = annotation;
+                      var beginTime = IriSP.msToTime(annotation.begin);
+                      var endTime = IriSP.msToTime(annotation.end);
+                      var timeTemplate = IriSP.templToHTML("- ({{begin}} - {{ end }})", {begin: beginTime, end: endTime });
+                      _this.selector.find(".Ldt-createAnnotation-TimeFrame").html(timeTemplate);
+              } }(annotation)
+            };
+      
+      this._Popcorn.code(code);
+    }
+  }
+  
+  this.selector.find(".Ldt-createAnnotation-submitButton").click(IriSP.wrap(this, this.handleButtonClick));
+  
+  if (!this.cinecast_version) {
+    this._Popcorn.listen("IriSP.PlayerWidget.AnnotateButton.clicked", 
+                          IriSP.wrap(this, this.handleAnnotateSignal));
+    
+    // handle clicks on the cancel button too.
+    this.selector.find(".Ldt-createAnnotation-Minimize").click(IriSP.wrap(this, 
+      function() {
+        // we've got to simulate the pressing of the button because there's no
+        // other way to minimize the widget and show the widgets that were hidden
+        // same time
+        this._Popcorn.trigger("IriSP.PlayerWidget.AnnotateButton.clicked");
+      }
+    ));
+  }
+};
+
+/** handles clicks on the annotate button. Works only for the non-cinecast version */
+IriSP.createAnnotationWidget.prototype.handleAnnotateSignal = function() {
+  
+  if (this._hidden == false && this._state == 'startScreen') {
+    this.selector.hide();
+    this._hidden = true;
+    
+    // free the arrow.
+    this._Popcorn.trigger("IriSP.ArrowWidget.releaseArrow");
+    this._Popcorn.trigger("IriSP.SliceWidget.hide");
+    this._Popcorn.trigger("IriSP.AnnotationsWidget.show");
+    
+  } else {
+    this._Popcorn.trigger("IriSP.AnnotationsWidget.hide");
+    this.showStartScreen();    
+    this.selector.show();
+    this._hidden = false;
+    var currentTime = this._Popcorn.currentTime();
+    
+    // block the arrow.
+    this._Popcorn.trigger("IriSP.ArrowWidget.blockArrow");
+    
+    var duration = +this._serializer.currentMedia().meta["dc:duration"];
+        
+    var currentChapter = this._serializer.currentChapitre(currentTime);
+
+    if (IriSP.null_or_undefined(currentChapter)) {      
+      var left = this.selector.width() / 2;
+      var width = this.selector.width() / 10;
+    } else {
+      var left = (currentChapter.begin / duration) * this.selector.width();
+      var width = (currentChapter.end / duration) * this.selector.width() - left;
+    }
+    
+    // slider position and length is kept in percents.
+    this.sliceLeft = (left / this.selector.width()) * 100;
+    this.sliceWidth = (width / this.selector.width()) * 100;
+    
+    this._Popcorn.trigger("IriSP.SliceWidget.position", [left, width]);
+    this._Popcorn.listen("IriSP.SliceWidget.zoneChange", IriSP.wrap(this, this.handleSliderChanges));
+    this._Popcorn.trigger("IriSP.SliceWidget.show");
+    
+    if (!IriSP.null_or_undefined(currentChapter)) {
+      this.selector.find(".Ldt-createAnnotation-Title").html(currentChapter.content.title);
+
+      this._currentcurrentChapter = currentChapter;
+      var beginTime = IriSP.msToTime(currentChapter.begin);
+      var endTime = IriSP.msToTime(currentChapter.end);
+      var timeTemplate = IriSP.templToHTML("- ({{begin}} - {{ end }})", {begin: beginTime, end: endTime });
+      this.selector.find(".Ldt-createAnnotation-TimeFrame").html(timeTemplate);
+    }
+  }
+};
+
+
+/** watch for changes in the textfield and change the buttons accordingly */
+IriSP.createAnnotationWidget.prototype.handleTextChanges = function(event) {
+  var contents = this.selector.find(".Ldt-createAnnotation-Description").val();
+
+  for(var keyword in this.ids) {
+  
+    var id = this.ids[keyword];
+
+    if (contents.indexOf(keyword) != -1) {
+      /* the word is present in the textarea but the button is not toggled */
+      if (this.selector.find("#" + id).hasClass("Ldt-createAnnotation-absent-keyword"))
+          this.selector.find("#" + id).removeClass("Ldt-createAnnotation-absent-keyword")
+                                      .addClass("Ldt-createAnnotation-present-keyword");      
+    } else {
+      /* the word is absent from the textarea but the button is toggled */
+      if (this.selector.find("#" + id).hasClass("Ldt-createAnnotation-present-keyword")) {
+          this.selector.find("#" + id).removeClass("Ldt-createAnnotation-present-keyword")
+                                      .addClass("Ldt-createAnnotation-absent-keyword");
+      }
+    }
+  }
+  
+  if (this.polemic_mode) {
+    /* Also go through the polemics to highlight the buttons */
+    for (var polemic in this.polemics) {
+      /* Add the active class to the button */
+      var classname = "Ldt-createAnnotation-polemic-" + this.polemics[polemic];
+      if (contents.indexOf(polemic) != -1) {        
+        this.selector.find("." + classname).addClass("Ldt-createAnnotation-polemic-active");
+      } else {
+        if (this.selector.find("." + classname).addClass("Ldt-createAnnotation-polemic-active"))
+          this.selector.find("." + classname).removeClass("Ldt-createAnnotation-polemic-active")
+      }
+    }
+  }
+};
+
+IriSP.createAnnotationWidget.prototype.showStartScreen = function() {
+  this.selector.find(".Ldt-createAnnotation-DoubleBorder").children().hide();
+  this.selector.find(".Ldt-createAnnotation-startScreen").show();
+  
+  var jqTextfield = this.selector.find(".Ldt-createAnnotation-Description"); // handle on the textfield. used for the closure
+  
+  /* test if the browser supports the placeholder attribute */
+  if (!IriSP.null_or_undefined(jqTextfield.get(0).placeholder)) {
+    jqTextfield.attr("placeholder", "Type your annotation here."); 
+  } else {
+    jqTextfield.val("Type your annotation here.");
+    jqTextfield.one("click", IriSP.wrap(this, function() { jqTextfield.val(""); }));    
+  }
+  
+ 
+  
+  this._state = "startScreen";
+};
+
+IriSP.createAnnotationWidget.prototype.showWaitScreen = function() {
+  this.selector.find(".Ldt-createAnnotation-DoubleBorder").children().hide();
+  this.selector.find(".Ldt-createAnnotation-waitScreen").show();
+  this._state = "waitScreen";
+};
+
+IriSP.createAnnotationWidget.prototype.showErrorScreen = function() {
+  this.selector.find(".Ldt-createAnnotation-DoubleBorder").children().hide();
+  this.selector.find(".Ldt-createAnnotation-errorScreen").show();
+  this._state = "errorScreen";
+};
+
+/** update show the final screen with links to share the created annotation */
+IriSP.createAnnotationWidget.prototype.showEndScreen = function(annotation) {
+  this.selector.find(".Ldt-createAnnotation-DoubleBorder").children().hide();
+  
+  if (this.cinecast_version) {
+    this.selector.find(".Ldt-createAnnotation-Title").parent().show();      
+  }
+
+  var url = document.location.href + "#id=" + annotation.id;
+  var twStatus = IriSP.mkTweetUrl(url);
+  var gpStatus = IriSP.mkGplusUrl(url);
+  var fbStatus = IriSP.mkFbUrl(url);
+  
+  this.selector.find(".Ldt-createAnnotation-endScreen-TweetLink").attr("href", twStatus);
+  this.selector.find(".Ldt-createAnnotation-endScreen-FbLink").attr("href", fbStatus);
+  this.selector.find(".Ldt-createAnnotation-endScreen-GplusLink").attr("href", gpStatus);
+          
+  this.selector.find(".Ldt-createAnnotation-endScreen").show();
+  this._state = "endScreen";
+};
+
+/** handle clicks on "send annotation" button */
+IriSP.createAnnotationWidget.prototype.handleButtonClick = function(event) {
+  var _this = this;
+  var textfield = this.selector.find(".Ldt-createAnnotation-Description");
+  var contents = textfield.val();
+
+  if (contents === "") {  
+    if (this.selector.find(".Ldt-createAnnotation-errorMessage").length === 0) {
+      this.selector.find(".Ldt-createAnnotation-Container")
+                   .after(IriSP.templToHTML(IriSP.createAnnotation_errorMessage_template));
+      textfield.css("background-color", "#d93c71");      
+    } else {      
+      this.selector.find(".Ldt-createAnnotation-errorMessage").show();
+    }
+
+      textfield.one("js_mod propertychange keyup input paste", IriSP.wrap(this, function() {
+                      var contents = textfield.val();
+                      
+                      if (contents !== "") {
+                        this.selector.find(".Ldt-createAnnotation-errorMessage").hide();
+                        textfield.css("background-color", "");
+                      }
+                   }));
+  } else {
+    this.showWaitScreen();
+    
+    this.sendLdtData(contents, function(annotation) {
+                      if (_this.cinecast_version) {
+                          if (_this._Popcorn.media.paused)
+                            _this._Popcorn.play();
+                      }
+
+                      if (_this._state == "waitScreen") {
+                        _this.showEndScreen(annotation);
+                        if (_this.cinecast_version) {
+                          window.setTimeout(IriSP.wrap(_this, function() { this.showStartScreen(); }), 5000);
+                        }
+                      }
+                      // hide the slicer widget
+                      if (!_this.cinecast_version) {                      
+                        _this._Popcorn.trigger("IriSP.SliceWidget.hide");
+                      }           
+                    });
+  }
+};
+
+IriSP.createAnnotationWidget.prototype.handleSliderChanges = function(params) {
+  this.sliceLeft = params[0];
+  this.sliceWidth = params[1];
+};
+
+IriSP.createAnnotationWidget.prototype.sendLdtData = function(contents, callback) {
+  var _this = this;
+  var apiJson = {annotations : [{}], meta: {}};
+  var annotation = apiJson["annotations"][0];
+  
+  annotation["media"] = this._serializer.currentMedia()["id"];
+  var duration_part = Math.round(this._serializer.currentMedia().meta["dc:duration"] / 20);
+  
+  if (this.cinecast_version) {   
+      annotation["begin"] = Math.round(this._Popcorn.currentTime() * 1000 - duration_part);
+      annotation["end"] = Math.round(this._Popcorn.currentTime() * 1000 + duration_part);      
+  } else {
+    var duration = +this._serializer.currentMedia().meta["dc:duration"];    
+    annotation["begin"] = +((duration * (this.sliceLeft / 100)).toFixed(0));
+    annotation["end"] = +((duration * ((this.sliceWidth + this.sliceLeft) / 100)).toFixed(0));
+  }
+
+  // boundary checks
+  if (annotation["begin"] < 0)
+        annotation["begin"] = 0;
+  
+  if (annotation["end"] > this._serializer.currentMedia().meta["dc:duration"])
+    annotation["end"] = this._serializer.currentMedia().meta["dc:duration"];
+      
+  
+  annotation["type"] = this._serializer.getContributions();
+  if (typeof(annotation["type"]) === "undefined")
+    annotation["type"] = "";
+  
+  annotation["type_title"] = "Contributions";
+  annotation.content = {};
+  annotation.content["data"] = contents;
+  
+  var meta = apiJson["meta"];
+  if (!IriSP.null_or_undefined(IriSP.user) && !IriSP.null_or_undefined(IriSP.user.name))
+    meta.creator = IriSP.user.name;    
+  else 
+    meta.creator = "An User";
+  
+  meta.created = Date().toString();
+  
+  annotation["tags"] = [];
+  
+  for (var i = 0; i < this.keywords.length; i++) {
+    var keyword = this.keywords[i];
+    if (contents.indexOf(keyword) != -1)
+      annotation["tags"].push(keyword);
+  }
+  
+  var jsonString = JSON.stringify(apiJson);
+  var project_id = this._serializer._data.meta.id;
+  
+  //TODO: extract magic url
+  var url = Mustache.to_html(this.api_endpoint_template,
+                              {id: project_id});
+                          
+  IriSP.jQuery.ajax({
+      url: url,
+      type: 'PUT',
+      contentType: 'application/json',
+      data: jsonString,               
+      //dataType: 'json',
+      success: IriSP.wrap(this, function(json, textStatus, XMLHttpRequest) {                   
+                    /* add the annotation to the annotation and tell the world */
+                    
+                    /* if the media doesn't have a contributions line, we need to add one */
+                    if (typeof(this._serializer.getContributions()) === "undefined") {
+                      /* set up a basic view */
+                      var tmp_view = {"dc:contributor": "perso", "dc:creator": "perso", "dc:title": "Contributions",
+                                      "id": json.annotations[0].type}
+
+                      this._serializer._data["annotation-types"].push(tmp_view);
+                    }
+                    
+                    delete annotation.tags;
+                    annotation.content.description = annotation.content.data;
+                    annotation.content.title = "";
+                    delete annotation.content.data;
+                    annotation.id = json.annotations[0].id;
+
+                    annotation.meta = meta;
+                    annotation.meta["id-ref"] = json.annotations[0]["type"];
+                    
+                    // everything is shared so there's no need to propagate the change
+                    _this._serializer._data.annotations.push(annotation);
+ 
+                    _this._Popcorn.trigger("IriSP.createAnnotationWidget.addedAnnotation", annotation);
+                    callback(annotation);
+      }), 
+      error: 
+              function(jqXHR, textStatus, errorThrown) { 
+                            console.log("an error occured while contacting " 
+                            + url + " and sending " + jsonString + textStatus ); 
+                            _this.showErrorScreen(); } });
+};IriSP.HelloWorldWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+}
+
+IriSP.HelloWorldWidget.prototype = new IriSP.Widget();
+
+IriSP.HelloWorldWidget.prototype.draw = function() {
+    this.selector
+        .html('Hello, world')
+        .css({
+            "text-align" : "center",
+            "padding": "10px 0",
+            "font-size" : "14px"
+        });
+        
+    console.log(this);
+}
+IriSP.PlayerWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  
+  this._searchBlockOpen = false;
+  this._searchLastValue = "";
+};
+
+IriSP.PlayerWidget.prototype = new IriSP.Widget();
+
+IriSP.PlayerWidget.prototype.draw = function() {
+  var self = this;
+  var width = this.width;
+	var height = this.height;
+	var heightS = this.height-20;
+	  
+	var playerTempl = IriSP.templToHTML(IriSP.player_template, {"share_template" : IriSP.share_template});
+  this.selector.append(playerTempl);		
+	
+  this.selector.children(".Ldt-controler").show();
+    
+  // handle clicks by the user on the video.
+  this._Popcorn.listen("play", IriSP.wrap(this, this.playButtonUpdater));
+  this._Popcorn.listen("pause", IriSP.wrap(this, this.playButtonUpdater));
+  
+  this._Popcorn.listen("volumechange", IriSP.wrap(this, this.muteButtonUpdater));
+
+  this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeDisplayUpdater));  
+  // update the time display for the first time.
+  this._Popcorn.listen("loadedmetadata", IriSP.wrap(this, this.timeDisplayUpdater));
+  
+  this._Popcorn.listen("IriSP.search.matchFound", IriSP.wrap(this, this.searchMatch));
+  this._Popcorn.listen("IriSP.search.noMatchFound", IriSP.wrap(this, this.searchNoMatch));
+  this._Popcorn.listen("IriSP.search.triggeredSearch", IriSP.wrap(this, this.triggeredSearch));
+  
+  
+  this.selector.find(".Ldt-CtrlPlay").click(function() { self.playHandler.call(self); });
+  this.selector.find(".Ldt-CtrlAnnotate").click(function() 
+                                            { self._Popcorn.trigger("IriSP.PlayerWidget.AnnotateButton.clicked"); });
+  this.selector.find(".Ldt-CtrlSearch").click(function() { self.searchButtonHandler.call(self); });
+  
+  this.selector.find('.Ldt-CtrlSound').click(function() { self.muteHandler.call(self); } );
+
+  this.selector.find(".Ldt-CtrlPlay").attr( "style", "background-color:#CD21C24;" );
+  
+  /*
+  var searchButtonPos = this.selector.find(".Ldt-CtrlSearch").position();
+  var searchBox = Mustache.to_html(IriSP.search_template, {margin_left : searchButtonPos.left + "px"});
+  this.selector.find(".Ldt-CtrlSearch").after(searchBox);
+  */
+  
+  // trigger an IriSP.PlayerWidget.MouseOver to the widgets that are interested (i.e : sliderWidget)
+  this.selector.hover(function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOver"); }, 
+                      function() { self._Popcorn.trigger("IriSP.PlayerWidget.MouseOut"); });
+ 
+  this.muteButtonUpdater(); /* some player - jwplayer notable - save the state of the mute button between sessions */
+};
+
+/* Update the elasped time div */
+IriSP.PlayerWidget.prototype.timeDisplayUpdater = function() {
+  
+  if (this._previousSecond === undefined)
+    this._previousSecond = this._Popcorn.roundTime();
+  
+  else {
+    /* we're still in the same second, so it's not necessary to update time */
+    if (this._Popcorn.roundTime() == this._previousSecond)
+      return;
+      
+  }
+  
+  // we get it at each call because it may change.
+  var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000; 
+  var totalTime = IriSP.secondsToTime(duration);
+  var elapsedTime = IriSP.secondsToTime(this._Popcorn.currentTime());
+  
+  this.selector.find(".Ldt-ElapsedTime").html(elapsedTime.toString());
+  this.selector.find(".Ldt-TotalTime").html(totalTime.toString());
+  this._previousSecond = this._Popcorn.roundTime();
+};
+
+/* update the icon of the button - separate function from playHandler
+   because in some cases (for instance, when the user directly clicks on
+   the jwplayer window) we have to change the icon without playing/pausing
+*/
+IriSP.PlayerWidget.prototype.playButtonUpdater = function() {
+  var status = this._Popcorn.media.paused;
+  
+  if ( status == true ){
+    /* the background sprite is changed by adding/removing the correct classes */
+    this.selector.find(".Ldt-CtrlPlay").attr("title", "Play");
+    this.selector.find(".Ldt-CtrlPlay").removeClass("Ldt-CtrlPlay-PauseState").addClass("Ldt-CtrlPlay-PlayState");
+  } else {
+    this.selector.find(".Ldt-CtrlPlay").attr("title", "Pause");
+    this.selector.find(".Ldt-CtrlPlay").removeClass("Ldt-CtrlPlay-PlayState").addClass("Ldt-CtrlPlay-PauseState");
+  }  
+
+  return;
+};
+
+
+IriSP.PlayerWidget.prototype.playHandler = function() {
+  var status = this._Popcorn.media.paused;
+  
+  if ( status == true ){        
+    this._Popcorn.play();   
+  } else {
+    this._Popcorn.pause();
+  }  
+};
+
+IriSP.PlayerWidget.prototype.muteHandler = function() {
+  if (!this._Popcorn.muted()) {    
+      this._Popcorn.mute(true);
+    } else {
+      this._Popcorn.mute(false);
+    }
+};
+
+IriSP.PlayerWidget.prototype.muteButtonUpdater = function() {
+  var status = this._Popcorn.media.muted;
+  
+  if ( status == true ){        
+    this.selector.find(".Ldt-CtrlSound").attr("title", "Unmute");
+    this.selector.find(".Ldt-CtrlSound").removeClass("Ldt-CtrlSound-MuteState").addClass("Ldt-CtrlSound-SoundState");    
+  } else {
+    this.selector.find(".Ldt-CtrlSound").attr("title", "Mute");
+    this.selector.find(".Ldt-CtrlSound").removeClass("Ldt-CtrlSound-SoundState").addClass("Ldt-CtrlSound-MuteState");
+  }  
+
+  return;
+};
+
+IriSP.PlayerWidget.prototype.showSearchBlock = function() {
+  var self = this;
+  
+  if (this._searchBlockOpen == false) {
+    this.selector.find(".LdtSearch").show("blind", { direction: "horizontal"}, 100);
+    this.selector.find(".LdtSearchInput").css('background-color','#fff');
+   
+    this._searchBlockOpen = true;           
+    this.selector.find(".LdtSearchInput").bind('keyup', null, function() { self.searchHandler.call(self); } );
+    this.selector.find(".LdtSearchInput").focus();
+    
+    // we need this variable because some widget can find a match in
+    // their data while at the same time other's don't. As we want the
+    // search field to become green when there's a match, we need a 
+    // variable to remember that we had one.
+    this._positiveMatch = false;
+
+    // tell the world the field is open
+    this._Popcorn.trigger("IriSP.search.open");     
+	}
+};
+
+IriSP.PlayerWidget.prototype.hideSearchBlock = function() {
+ if (this._searchBlockOpen == true) {
+    this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value');
+    this.selector.find(".LdtSearchInput").attr('value','');
+    this.selector.find(".LdtSearch").hide("blind", { direction: "horizontal"}, 75);
+    
+    // unbind the watcher event.
+    this.selector.find(".LdtSearchInput").unbind('keypress set');
+    this._searchBlockOpen = false;
+
+    this._positiveMatch = false;
+    
+    this._Popcorn.trigger("IriSP.search.closed");
+	}
+};
+
+/** react to clicks on the search button */
+IriSP.PlayerWidget.prototype.searchButtonHandler = function() {
+  var self = this;
+
+  /* show the search field if it is not shown */
+  if ( this._searchBlockOpen == false ) {
+    this.showSearchBlock();
+    this.selector.find(".LdtSearchInput").attr('value', this._searchLastValue);      
+    this._Popcorn.trigger("IriSP.search", this._searchLastValue); // trigger the search to make it more natural.
+	} else {
+    this.hideSearchBlock();
+  }
+};
+
+/** this handler is called whenever the content of the search
+   field changes */
+IriSP.PlayerWidget.prototype.searchHandler = function() {
+  this._searchLastValue = this.selector.find(".LdtSearchInput").attr('value');
+  this._positiveMatch = false;
+  
+  // do nothing if the search field is empty, instead of highlighting everything.
+  if (this._searchLastValue == "") {
+    this._Popcorn.trigger("IriSP.search.cleared");
+    this.selector.find(".LdtSearchInput").css('background-color','');
+  } else {
+    this._Popcorn.trigger("IriSP.search", this._searchLastValue);
+  }
+};
+
+/**
+  handler for the IriSP.search.found message, which is sent by some views when they
+  highlight a match.
+*/
+IriSP.PlayerWidget.prototype.searchMatch = function() {
+  this._positiveMatch = true;
+  this.selector.find(".LdtSearchInput").css('background-color','#e1ffe1');
+};
+
+/** the same, except that no value could be found */
+IriSP.PlayerWidget.prototype.searchNoMatch = function() {
+  if (this._positiveMatch !== true)
+    this.selector.find(".LdtSearchInput").css('background-color', "#d62e3a");
+};
+
+/** react to an IriSP.Player.triggeredSearch - that is, when
+    a widget ask the PlayerWidget to do a search on his behalf */
+IriSP.PlayerWidget.prototype.triggeredSearch = function(searchString) {
+  this.showSearchBlock();
+  this.selector.find(".LdtSearchInput").attr('value', searchString);      
+  this._Popcorn.trigger("IriSP.search", searchString); // trigger the search to make it more natural.
+};
+
+
+/* 
+ *   
+ *  Copyright 2010 Institut de recherche et d'innovation 
+ *  contributor(s) : Samuel Huron 
+ *   
+ *  contact@iri.centrepompidou.fr
+ *  http://www.iri.centrepompidou.fr 
+ *   
+ *  This software is a computer program whose purpose is to show and add annotations on a video .
+ *  This software is governed by the CeCILL-C license under French law and
+ *  abiding by the rules of distribution of free software. You can  use, 
+ *  modify and/ or redistribute the software under the terms of the CeCILL-C
+ *  license as circulated by CEA, CNRS and INRIA at the following URL
+ *  "http://www.cecill.info". 
+ *  
+ *  The fact that you are presently reading this means that you have had
+ *  knowledge of the CeCILL-C license and that you accept its terms.
+*/
+// CHART TIMELINE / VERSION PROTOTYPE  ::
+
+/** the polemic widget */
+IriSP.PolemicWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+ 
+  this.userPol    = new Array();
+  this.userNoPol  = new Array();
+  this.userst      = new Array();
+  this.numberOfTweet = 0;
+  this.Users;
+  this.TweetPolemic;
+  this.yMax        = this.height; 
+  this.PaperSlider;
+  this.heightOfChart;
+  this.tweets  = new Array();
+  this.svgElements = {};
+  
+  this.oldSearchMatches = [];
+};
+
+IriSP.PolemicWidget.prototype = new IriSP.Widget();
+  
+IriSP.PolemicWidget.prototype.draw = function() {
+  
+    // variable 
+    // yMax
+    
+    var self = this;
+    var yCoef        = 2;             // coef for height of 1 tweet 
+    var frameSize     = 5;             // frame size 
+    var margin         = 1;            // marge between frame
+    var lineSize      = this.width;        // timeline pixel width 
+    var nbrframes     = lineSize/frameSize;     // frame numbers
+    var numberOfTweet   = 0;            // number of tweet overide later 
+    var duration      = +this._serializer.currentMedia().meta["dc:duration"];      // timescale width 
+    var frameLength   = lineSize / frameSize;    // frame timescale  
+    var timeline;
+    var colors  = new Array("","#1D973D","#C5A62D","#CE0A15","#036AAE","#585858");
+    
+    // array 
+    //var tweets  = new Array();
+    var element = new Array();
+    var cluster = new Array();
+    var frames  = new Array(frameLength);
+    var slices  = new Array();
+    
+    
+    // Classes =======================================================================
+    var Frames = function(){
+      
+      var Myclusters;
+      var x;
+      var y;
+      var width;
+      var height;
+    };
+    Frames = function(json){
+      // make my clusters
+      // ou Frame vide 
+    };
+    Frames.prototype.draw = function(){
+    };
+    Frames.prototype.zoom = function(){
+    };
+    Frames.prototype.inside = function(){
+    };
+    var Clusters = function(){
+      var Object;
+      var yDist;
+      var x;
+      var y;
+      var width;
+      var height;
+    };
+    Clusters = function(json){
+      // make my object
+    };
+    var Tweet = function(){
+    };
+    // Classes =======================================================================
+
+    // Refactoring (parametere) ************************************************************
+    // color translastion
+    var qTweet_0  =0;
+    var qTweet_Q  =0;
+    var qTweet_REF=0;
+    var qTweet_OK =0;
+    var qTweet_KO =0;
+    function colorTranslation(value){
+      if(value == "Q"){
+        qTweet_Q+=1;
+        return 2;
+      }else if(value =="REF"){
+        qTweet_REF+=1;
+        return 4;
+      }else if(value =="OK"){
+        qTweet_OK+=1;
+        return 1;
+      }else if(value =="KO"){
+        qTweet_KO+=1;
+        return 3;
+      }else if(value ==""){
+        qTweet_0+=1;
+        return 5;
+      }
+    }
+    
+
+      this._serializer.sync(function(data) { loaded_callback.call(self, data); return; });
+      
+      function loaded_callback (json) {
+      var view_type = this._serializer.getTweets();
+
+      
+      if (typeof(view_type) === "undefined") {
+        var view_type = this._serializer.getTweetIds()[0];
+        if (typeof(view_type) === "undefined") {
+          // default to guessing if nothing else works.
+          var view = json.views[0];
+          
+          if(typeof(view.annotation_types) !== "undefined") {
+            /* we need to be backward compatible with the old files which used to
+               feature only two lines : Chapitrage and Tweets. We've added a
+               "Contributions" line so we need to discriminate against that */
+            if (view.annotation_types.length === 2 && typeof(this._serializer.getContributions()) === "undefined") {
+              var view_type = view.annotation_types[1];
+            } else {
+              console.log("PolemicWidget: invalid file - minimizing");
+              return;
+            }
+          }      
+        }
+      }
+      
+      // Make and define the Raphael area
+      this.paper = Raphael(document.getElementById(this._id), this._config.width, this._config.height);
+      
+      // event handlers
+      this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) { this.searchHandler(searchString); }));
+      this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.searchFieldClosedHandler));
+      this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.searchFieldClearedHandler));
+      this.selector.mouseleave(IriSP.wrap(this, function() { self.TooltipWidget.hide.call(self.TooltipWidget); }));
+      this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater));
+      this._Popcorn.listen("IriSP.Mediafragment.showAnnotation", IriSP.wrap(this, this.showAnnotation));
+      
+      for(var i = 0; i < json.annotations.length; i++) {
+        var item = json.annotations[i];        
+        var MyTime  = Math.floor(item.begin/duration*lineSize);
+        var Myframe = Math.floor(MyTime/lineSize*frameLength);
+
+        if (typeof(item.meta) !== "undefined" 
+          && typeof(item.meta["id-ref"]) !== "undefined"
+          && item.meta["id-ref"] === view_type) {
+            
+            var MyTJson = {};
+            if (typeof(item.meta['dc:source']) !== "undefined") {
+              var MyTJson = JSON.parse(item.meta['dc:source']['content']);
+            }
+            
+            if (item.content['polemics'] != undefined 
+            && item.content['polemics'][0] != null) {
+            
+              // a tweet can have many polemics at the same time.
+              for(var j=0; j<item.content['polemics'].length; j++){
+                  
+                  this.tweets[numberOfTweet] = {
+                        id:i,
+                        qualification:colorTranslation(item.content['polemics'][j]),
+                        yIndicator:MyTime,
+                        yframe:Myframe,
+                        title:item.content['title'],
+                        timeframe:item.begin,
+                        userId: MyTJson.id,
+                        userScreenName: MyTJson.screen_name,
+                        tsource:MyTJson,
+                        cinecast_id: item.id
+                        };
+                  numberOfTweet+=1;
+                  
+              }
+          }
+          else {
+            this.tweets[numberOfTweet] = {
+                  id:i,
+                  qualification:colorTranslation(""),
+                  yIndicator:MyTime,
+                  yframe:Myframe,
+                  title:item.content['title'],
+                  timeframe:item.begin,
+                  userId: MyTJson.id,
+                  userScreenName: MyTJson.screen_name,
+                  tsource:MyTJson,
+                  cinecast_id: item.id
+            };
+            numberOfTweet+=1;
+          }
+          
+        } 
+      };  
+      
+       DrawTweets.call (this); // FIXME: ugly.
+       
+      };      
+
+    // tweet Drawing (in raphael) 
+    function DrawTweets (){
+    // GROUPES TWEET ============================================
+    // Count nbr of cluster and tweet in a frame an save int in "frames"
+      numberOfTweet = this.tweets.length;
+      for(var i=0; i<nbrframes; i++) {  
+        for(var j=0; j<numberOfTweet; j++) {  
+        
+          if (i==this.tweets[j].yframe){
+            
+            var k = this.tweets[j].qualification;
+            
+            // make array for frame cluster
+            if(frames[i]==undefined){
+              frames[i] = {id:i,
+                     qualifVol:new Array(),
+                     mytweetsID:new Array()
+                    };
+            }
+            // add my tweet to frame
+            frames[i].mytweetsID.push(this.tweets[j]);
+            
+            // count opinion by frame
+            if( frames[i].qualifVol[k] == undefined){
+              frames[i].qualifVol[k] = 1;
+            }else{
+              frames[i].qualifVol[k] += 1;
+            }
+            
+          }
+        }
+      }
+    
+    // GROUPES TWEET ============================================    
+    // max of tweet by Frame 
+      var max = 0; 
+      for(var i = 0; i < nbrframes; i++) {
+        var moy  = 0;
+        for (var j = 0; j < 6; j++) {    
+          if (frames[i] != undefined) {
+            if (frames[i].qualifVol[j] != undefined) {
+              moy += frames[i].qualifVol[j];
+            }
+          }
+        }
+        
+        if (moy > max) {
+          max = moy;
+        }
+      }
+    
+      var tweetDrawed = new Array();
+      var TweetHeight = 5;
+      var newHeight = TweetHeight * max + 10;
+
+      
+      if (newHeight > this.height) {
+        this.paper.setSize(this.width, newHeight);
+        this.height = newHeight;
+        console.log("resizeing");
+      }
+      
+  
+      // DRAW  TWEETS ============================================
+      for(var i = 0; i < nbrframes; i++) {
+        var addEheight = 5;
+        if (frames[i] != undefined){                
+          // by type 
+          
+          for (var j = 6; j > -1; j--) {
+            if (frames[i].qualifVol[j] != undefined) {
+              // show tweet by type 
+              for (var k = 0; k < frames[i].mytweetsID.length; k++) {
+              
+                if (frames[i].mytweetsID[k].qualification == j) {                
+                  var x = i * frameSize;
+                  var y = this.height - addEheight;
+                  
+                  if (this.yMax > y) {
+                    this.yMax = y;
+                  }
+                  
+                  /* some tweets seem to be duplicated - so we make a check before
+                     creating a new rect */
+                  if (this.svgElements.hasOwnProperty(frames[i].mytweetsID[k].cinecast_id))
+                    continue;
+                  
+                  var e = this.paper.rect(x, y, frameSize - margin, TweetHeight /* height */)
+                                    .attr({stroke:"#00","stroke-width":0.1,  fill: colors[j]});  
+                  
+                  addEheight += TweetHeight;
+                  
+                  /* stick a lot of things into e because that's the easiest way
+                     to do it */
+                  e.color = colors[j];
+                  e.time = frames[i].mytweetsID[k].timeframe;
+                  e.title = frames[i].mytweetsID[k].title;
+                  e.id = frames[i].mytweetsID[k].cinecast_id;
+                  var pos = IriSP.jQuery(e.node).offset();                  
+                  e.x = pos.left;
+                  e.y = pos.top;
+                  this.svgElements[e.id] = e;
+                  
+                  IriSP.jQuery(e.node).mouseenter(function(element) { return function (event) {                        
+                        // event.clientX and event.clientY are to raphael what event.pageX and pageY are to jquery.                        
+                        self.TooltipWidget.show.call(self.TooltipWidget, element.title, element.attr("fill"), event.pageX - 106, event.pageY - 160);
+                        element.displayed = true;
+                  }}(e)).mousedown(function(element) { return function () {                    
+                    self._Popcorn.currentTime(element.time/1000);
+                    self._Popcorn.trigger("IriSP.PolemicTweet.click", element.id); 
+                    }
+                  }(e));                  
+                  
+                  IriSP.jQuery(e.node).attr('id', 't' + k + '');
+                  IriSP.jQuery(e.node).attr('title', frames[i].mytweetsID[k].title);
+                  IriSP.jQuery(e.node).attr('begin',  frames[i].mytweetsID[k].timeframe);                  
+                }
+              }
+            }
+          }
+        }
+
+      }    
+      // DRAW UI :: resize border and bgd      
+      this.paperBackground = this.paper.rect(0, 0, this.width, this.height).attr({fill:"#F8F8F8","stroke-width":0.1,opacity: 1});  
+
+      // outer borders
+      this.outerBorders   = [];
+      this.outerBorders.push(this.paper.rect(0, this.height - 1, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1}));  
+      this.outerBorders.push(this.paper.rect(0, 0, this.width, 1).attr({fill:"#ababab",stroke: "none",opacity: 1}));  
+
+      // inner borders
+      this.innerBorders   = [];
+      this.innerBorders.push(this.paper.rect(1, this.height - 2, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
+      this.innerBorders.push(this.paper.rect(1, 1, this.width, 1).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
+      this.innerBorders.push(this.paper.rect(1, 1, 1, this.height - 2).attr({fill:"#d0d1d1",stroke: "none",opacity: 0.8}));  
+      this.innerBorders.push(this.paper.rect(this.width - 2, 1, 1, this.height - 2).attr({fill:"#efefef",stroke: "none",opacity: 1}));  
+
+
+
+      this.paperSlider   = this.paper.rect(0, 0, 0, this.height).attr({fill:"#D4D5D5", stroke: "none", opacity: 1});
+      
+      // the small white line displayed over the slider.
+      this.sliderTip = this.paper.rect(0, 0, 1, this.height).attr({fill:"#fc00ff", stroke: "none", opacity: 1});
+      // decalage 
+      // tweetSelection = this.paper.rect(-100,-100,5,5).attr({fill:"#fff",stroke: "none",opacity: 1});  
+      
+      
+      this.paperSlider.toBack();
+      this.paperBackground.toBack();
+      this.sliderTip.toFront();
+    }
+    
+
+}
+
+/** update the positionMarker as time passes */
+IriSP.PolemicWidget.prototype.sliderUpdater = function() {
+
+    var time = +this._Popcorn.currentTime();
+    var duration = +this._serializer.currentMedia().meta["dc:duration"];
+    
+    this.paperSlider.attr("width", time * (this.width / (duration / 1000)));
+        
+    this.sliderTip.attr("x", time * (this.width / (duration / 1000)));
+};
+
+/** reacts to IriSP.search events */    
+IriSP.PolemicWidget.prototype.searchHandler = function(searchString) {
+  if (searchString == "")
+    return;
+
+  var matches = this._serializer.searchTweetsOccurences(searchString);
+
+  if (IriSP.countProperties(matches) > 0) {
+    this._Popcorn.trigger("IriSP.search.matchFound");
+  } else {
+    this._Popcorn.trigger("IriSP.search.noMatchFound");
+  }
+
+  
+  // decrease the opacity of the other elements.
+  for (var id in this.svgElements) {
+    var e = this.svgElements[id];
+    e.attr({fill: e.color, opacity: 0.4});   
+  }
+  
+
+  for (var id in matches) {    
+    if (this.svgElements.hasOwnProperty(id)) {
+      var e = this.svgElements[id];
+      this.svgElements[id].attr({fill: "#fc00ff", opacity: 1});
+    }
+  }
+
+  this.oldSearchMatches = matches;
+};
+
+/** reacts to IriSP.search.cleared messages */
+IriSP.PolemicWidget.prototype.searchFieldClearedHandler = function() {
+  for (var id in this.svgElements) {
+    var e = this.svgElements[id];
+    e.attr({fill: e.color, opacity: 1});
+  }
+};
+
+/** reacts to IriSP.search.closed messages by clearing the highlighted elements */
+IriSP.PolemicWidget.prototype.searchFieldClosedHandler = function() {
+  for (var id in this.svgElements) {
+    var e = this.svgElements[id];
+    e.attr({fill: e.color, opacity: 1});
+  }
+ 
+};
+   
+IriSP.PolemicWidget.prototype.showAnnotation = function(id) {
+  if (this.svgElements.hasOwnProperty(id)) {
+    var e = this.svgElements[id];
+    this.TooltipWidget.show(e.title, e.attr("fill"), e.x - 103, e.y - 160);
+  }
+};   
+IriSP.SegmentsWidget = function(Popcorn, config, Serializer) {
+
+  var self = this;
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  this.oldSearchMatches = [];
+
+  // event handlers
+  this._Popcorn.listen("IriSP.search", function(searchString) { self.searchHandler.call(self, searchString); });
+  this._Popcorn.listen("IriSP.search.closed", function() { self.searchFieldClosedHandler.call(self); });
+  this._Popcorn.listen("IriSP.search.cleared", function() { self.searchFieldClearedHandler.call(self); });
+};
+
+IriSP.SegmentsWidget.prototype = new IriSP.Widget();
+
+/* Get the width of a segment, in pixels. */
+IriSP.SegmentsWidget.prototype.segmentToPixel = function(annotation) {  
+  var begin = Math.round((+ annotation.begin) / 1000);
+  var end = Math.round((+ annotation.end) / 1000);    
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  
+  var startPourcent 	= IriSP.timeToPourcent(begin, duration);
+  var startPixel = Math.floor(this.selector.parent().width() * (startPourcent / 100));
+  
+  var endPourcent 	= Math.floor(IriSP.timeToPourcent(end, duration) - startPourcent);
+  var endPixel = Math.floor(this.selector.parent().width() * (endPourcent / 100));
+  
+  return endPixel;
+};
+
+/* compute the total length of a group of segments */
+IriSP.SegmentsWidget.prototype.segmentsLength = function(segmentsList) {
+  var self = this;
+  var total = 0;
+  
+  for (var i = 0; i < segmentsList.length; i++)
+    total += self.segmentToPixel(segmentsList[i].annotation);
+  
+  return total;  
+};
+
+IriSP.SegmentsWidget.prototype.draw = function() {
+
+  var self = this;
+  var annotations = this._serializer._data.annotations;
+
+  this.selector.addClass("Ldt-SegmentsWidget");
+  this.selector.append(Mustache.to_html(IriSP.overlay_marker_template));
+          
+  var view_type = this._serializer.getChapitrage();
+  if (typeof(view_type) === "undefined") {
+    view_type = this._serializer.getNonTweetIds()[0];  
+  }
+  this.positionMarker = this.selector.children(":first");
+  
+  this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.positionUpdater));
+
+  
+  var i = 0;
+  
+  var segments_annotations = [];
+  
+  for (i = 0; i < annotations.length; i++) {
+    var annotation = annotations[i];
+
+    /* filter the annotations whose type is not the one we want */
+    if (view_type != "" && typeof(annotation.meta) !== "undefined" && typeof(annotation.meta["id-ref"]) !== "undefined"
+          && annotation.meta["id-ref"] != view_type) {
+        continue;
+    }
+
+    segments_annotations.push(annotation);
+  }
+    
+  var totalWidth = this.selector.width() - segments_annotations.length;
+  var lastSegment = IriSP.underscore.max(segments_annotations, function(annotation) { return annotation.end; });
+  
+  for (i = 0; i < segments_annotations.length; i++) {
+  
+    var annotation = segments_annotations[i];
+    var begin = (+ annotation.begin);
+    var end = (+ annotation.end);
+    var duration = this._serializer.currentMedia().meta["dc:duration"];
+    var id = annotation.id;
+        
+    var startPixel = Math.floor(this.selector.parent().width() * (begin / duration));
+
+    var endPixel = Math.floor(this.selector.parent().width() * (end / duration));
+    
+    if (annotation.id !== lastSegment.id) 
+      var pxWidth = endPixel - startPixel -1;
+    else
+      /* the last segment has no segment following it */
+      var pxWidth = endPixel - startPixel;
+ 
+    var divTitle = IriSP.clean_substr(annotation.content.title + " -<br>" + annotation.content.description, 0, 132) + "...";
+
+    if (typeof(annotation.content.color) !== "undefined")
+      var color = annotation.content.color;
+    else
+      var color = annotation.color;
+    
+    var hexa_color = IriSP.DEC_HEXA_COLOR(color);
+
+    /*
+    if (hexa_color === "FFCC00")
+      hexa_color = "333";
+    */
+    if (hexa_color.length == 4)
+      hexa_color = hexa_color + '00';
+    
+    var annotationTemplate = Mustache.to_html(IriSP.annotation_template,
+        {"divTitle" : divTitle, "id" : id, "startPixel" : startPixel,
+        "pxWidth" : pxWidth, "hexa_color" : hexa_color,
+        "seekPlace" : Math.round(begin/1000)});
+
+        
+    this.selector.append(annotationTemplate);
+    
+    /* add a special class to the last segment and change its border */
+    if (annotation.id === lastSegment.id) {
+        this.selector.find("#" + id).addClass("Ldt-lastSegment");        
+        this.selector.find(".Ldt-lastSegment").css("border-color", "#" + hexa_color);        
+    }
+
+    IriSP.jQuery("#" + id).fadeTo(0, 0.3);
+
+    IriSP.jQuery("#" + id).mouseover(
+    /* we wrap the handler in another function because js's scoping
+       rules are function-based - otherwise, the internal vars like
+       divTitle are preserved but they are looked-up from the draw
+       method scope, so after that the loop is run, so they're not
+       preserved */
+    (function(divTitle) { 
+     return function(event) {
+          IriSP.jQuery(this).animate({opacity: 0.6}, 5);
+          var offset = IriSP.jQuery(this).offset();
+          var correction = IriSP.jQuery(this).outerWidth() / 2;
+
+          var offset_x = offset.left + correction - 106;
+          if (offset_x < 0)
+            offset_x = 0;
+          
+          var offset_y = offset.top;          
+
+          self.TooltipWidget.show(divTitle, color, offset_x, offset_y - 160);
+    } })(divTitle)).mouseout(function(){
+      IriSP.jQuery(this).animate({opacity: 0.3}, 5);
+      self.TooltipWidget.hide();
+    });
+
+    // react to mediafragment messages.
+    this._Popcorn.listen("IriSP.Mediafragment.showAnnotation", 
+      function(id, divTitle) { 
+      return function(annotation_id) { 
+        if (annotation_id !== id)
+          return;
+        
+          var divObject = IriSP.jQuery("#" + id);
+          divObject.animate({opacity: 0.6}, 5);
+          var offset = divObject.offset();
+          var correction = divObject.outerWidth() / 2;
+
+          var offset_x = offset.left + correction - 106;
+          if (offset_x < 0)
+            offset_x = 0;
+          
+          var offset_y = offset.top;          
+
+          self.TooltipWidget.show(divTitle, color, offset_x, offset_y - 160);
+          IriSP.jQuery(document).one("mousemove", function() { divObject.animate({opacity: 0.3}, 5);
+                                                                self.TooltipWidget.hide(); });
+      }; }(id, divTitle));
+    
+    IriSP.jQuery("#" + id).click(function(_this, annotation) {
+                                    return function() { _this.clickHandler(annotation)};
+                                 }(this, annotation));
+    }
+};
+
+/* restores the view after a search */
+IriSP.SegmentsWidget.prototype.clear = function() {
+  this.selector.children(".Ldt-iri-chapter").animate({opacity:0.3}, 100);
+};
+
+IriSP.SegmentsWidget.prototype.clickHandler = function(annotation) {
+  this._Popcorn.trigger("IriSP.SegmentsWidget.click", annotation.id);
+  var begin = (+ annotation.begin) / 1000;
+  this._Popcorn.currentTime(Math.round(begin));
+};
+
+IriSP.SegmentsWidget.prototype.searchHandler = function(searchString) {
+
+  if (searchString == "")
+    return;
+
+  var matches = this._serializer.searchOccurences(searchString);
+
+  if (IriSP.countProperties(matches) > 0) {
+    this._Popcorn.trigger("IriSP.search.matchFound");
+  } else {
+    this._Popcorn.trigger("IriSP.search.noMatchFound");
+  }
+
+  // un-highlight all the blocks
+  this.selector.children(".Ldt-iri-chapter").css("opacity", 0.1);
+ 
+  // then highlight the ones with matches.
+  for (var id in matches) {
+    var factor = 0.5 + matches[id] * 0.2;
+    this.selector.find("#"+id).dequeue();
+    this.selector.find("#"+id).animate({opacity:factor}, 200);
+  }
+
+ 
+  this.oldSearchMatches = matches;
+};
+
+IriSP.SegmentsWidget.prototype.searchFieldClearedHandler = function() {
+  this.clear();
+};
+
+IriSP.SegmentsWidget.prototype.searchFieldClosedHandler = function() {
+  this.clear();
+};
+
+IriSP.SegmentsWidget.prototype.positionUpdater = function() {  
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var time = this._Popcorn.currentTime();
+  //var position 	= ((time / duration) * 100).toFixed(2);
+  var position 	= ((time / duration) * 100).toFixed(2);
+
+  this.positionMarker.css("left", position + "%");  
+};
+
+IriSP.SegmentsWidget.prototype.showAnnotation = function() {
+
+};
+/** A widget to create a new segment */
+IriSP.SliceWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  
+};
+
+IriSP.SliceWidget.prototype = new IriSP.Widget();
+
+IriSP.SliceWidget.prototype.draw = function() {
+  var templ = Mustache.to_html(IriSP.sliceWidget_template);
+  this.selector.append(templ);
+  
+  this.sliceZone = this.selector.find(".Ldt-sliceZone");
+  
+  /* global variables used to keep the position and width
+     of the zone.
+  */  
+  this.zoneLeft = 0;
+  this.zoneWidth = 0;
+  
+  this.leftHandle = this.selector.find(".Ldt-sliceLeftHandle");
+  this.rightHandle = this.selector.find(".Ldt-sliceRightHandle");
+
+  var left = this.selector.offset().left;
+  var top = this.selector.offset().top;
+
+  // a bug in firefox makes it use the wrong format
+  if (!IriSP.jQuery.browser.mozilla) {
+    // contain the handles correctly - we cannot set
+    // containment: parent because it wouldn't allow to select the 
+    // whole slice, so we have to compute a box in which the slice is
+    // allowed to move.
+    var containment = [left - 8, top, this.selector.width() + left, top];
+
+    // var containment = [left - 16, top, this.selector.width() + left - 8, top];
+    this.leftHandle.draggable({axis: "x",
+    drag: IriSP.wrap(this, this.leftHandleDragged),  
+    containment: containment
+    });
+
+    containment = [left, top, this.selector.width() + left, top];
+    // containment = [left, top, this.selector.width() + left - 8, top];
+    this.rightHandle.draggable({axis: "x",
+    drag: IriSP.wrap(this, this.rightHandleDragged),    
+    containment: containment
+    });
+  
+  } else { // firefox
+    // we need to define a containment specific to firefox.
+    
+    var containment = [left - 16, top, this.selector.width() + left - 8, top];
+    this.leftHandle.draggable({axis: "x",
+    drag: IriSP.wrap(this, this.leftHandleDragged),  
+    containment: containment
+    });
+
+    containment = [left, top, this.selector.width() + left - 8, top];
+    this.rightHandle.draggable({axis: "x",
+    drag: IriSP.wrap(this, this.rightHandleDragged),    
+    containment: containment
+    });
+  }
+  
+  this.leftHandle.css("position", "absolute");
+  this.rightHandle.css("position", "absolute");
+  
+  this._Popcorn.listen("IriSP.SliceWidget.position", 
+                        IriSP.wrap(this, this.positionSliceHandler));
+  
+  this._Popcorn.listen("IriSP.SliceWidget.show", IriSP.wrap(this, this.show));
+  this._Popcorn.listen("IriSP.SliceWidget.hide", IriSP.wrap(this, this.hide));
+  this.selector.hide();
+};
+
+/** responds to an "IriSP.SliceWidget.position" message
+    @param params an array with the first element being the left distance in
+           percents and the second element the width of the slice in pixels
+*/        
+IriSP.SliceWidget.prototype.positionSliceHandler = function(params) {
+  left = params[0];
+  width = params[1];
+  
+  this.zoneLeft = left;
+  this.zoneWidth = width;
+  this.sliceZone.css("left", left + "px");
+  this.sliceZone.css("width", width + "px");
+  this.leftHandle.css("left", (left - 7) + "px");
+  this.rightHandle.css("left", left + width + "px");
+  
+  this._leftHandleOldLeft = left - 7;
+  this._rightHandleOldLeft = left + width;
+};
+
+/** handle a dragging of the left handle */
+IriSP.SliceWidget.prototype.leftHandleDragged = function(event, ui) {
+  /* we have a special variable, this._leftHandleOldLeft, to keep the
+     previous position of the handle. We do that to know in what direction
+     is the handle being dragged
+  */
+  
+  var currentX = this.leftHandle.offset().left;
+  var rightHandleX = Math.floor(this.rightHandle.position()["left"]);
+  
+  var container_offset = this.selector.offset().left;
+
+  if (Math.floor(ui.position.left) >= rightHandleX - 7) {
+    /* prevent the handle from moving past the right handle */
+    ui.position.left = rightHandleX - 7;
+  }
+
+  this.zoneWidth = rightHandleX - Math.floor(ui.position.left) - 7;  
+  this.zoneLeft = Math.floor(ui.position.left) + 8;
+  
+  this.sliceZone.css("width", this.zoneWidth);
+  this.sliceZone.css("left", this.zoneLeft + "px");
+  
+  this._leftHandleOldLeft = ui.position.left;  
+  this.broadcastChanges();
+    
+};
+
+/** handle a dragging of the right handle */
+IriSP.SliceWidget.prototype.rightHandleDragged = function(event, ui) { 
+  /* we have a special variable, this._leftHandleOldLeft, to keep the
+     previous position of the handle. We do that to know in what direction
+     is the handle being dragged
+  */
+  
+  var currentX = this.leftHandle.position()["left"];
+  var leftHandleX = Math.floor(this.leftHandle.position()["left"]);
+
+  var container_offset = this.selector.offset().left + this.selector.width();
+  
+  if (Math.floor(ui.position.left) < leftHandleX + 7) {
+    /* prevent the handle from moving past the left handle */
+    ui.position.left = leftHandleX + 7;
+  }
+
+  this.zoneWidth = Math.floor(ui.position.left) - (leftHandleX + 7);    
+  
+  this.sliceZone.css("width", this.zoneWidth);
+  //this.sliceZone.css("left", this.zoneLeft + "px");
+  this._rightHandleOldLeft = Math.floor(this._rightHandleOldLeft);  
+  this.broadcastChanges();
+};
+
+/** tell to the world that the coordinates of the slice have
+    changed 
+*/
+IriSP.SliceWidget.prototype.broadcastChanges = function() {
+  var leftPercent = (this.zoneLeft / this.selector.width()) * 100;
+  var zonePercent = (this.zoneWidth / this.selector.width()) * 100;
+
+  this._Popcorn.trigger("IriSP.SliceWidget.zoneChange", [leftPercent, zonePercent]);  
+};
+
+IriSP.SliceWidget.prototype.show = function() {
+  this.selector.show();
+};
+
+IriSP.SliceWidget.prototype.hide = function() {
+  this.selector.hide();
+};IriSP.SliderWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+};
+
+IriSP.SliderWidget.prototype = new IriSP.Widget();
+
+IriSP.SliderWidget.prototype.draw = function() {
+  var self = this;
+
+  this.selector.append(Mustache.to_html(IriSP.sliderWidget_template, {}));
+  this.selector.addClass("Ldt-SliderMinimized");
+
+  this.sliderBackground = this.selector.find(".Ldt-sliderBackground");
+  this.sliderForeground = this.selector.find(".Ldt-sliderForeground");
+  this.positionMarker = this.selector.find(".Ldt-sliderPositionMarker");
+
+
+  // a special variable to stop methods from tinkering
+  // with the positionMarker when the user is dragging it
+  this.draggingOngoing = false;
+
+  // another special variable used by the timeout handler to
+  // open or close the slider.
+  this.sliderMaximized = false;
+  this.timeOutId = null;
+
+  
+  this.positionMarker.draggable({axis: "x",
+  start: IriSP.wrap(this, this.positionMarkerDraggingStartedHandler),
+  stop: IriSP.wrap(this, this.positionMarkerDraggedHandler),
+  containment: "parent"
+  });
+  this.positionMarker.css("position", "absolute");
+  
+  this.sliderBackground.click(function(event) { self.backgroundClickHandler.call(self, event); });
+  this.sliderForeground.click(function(event) { self.foregroundClickHandler.call(self, event); });
+
+  this.selector.hover(IriSP.wrap(this, this.mouseOverHandler), IriSP.wrap(this, this.mouseOutHandler));
+
+  // update the positions
+  this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.sliderUpdater));
+
+  // special messages :
+  this._Popcorn.listen("IriSP.PlayerWidget.MouseOver", IriSP.wrap(this, this.mouseOverHandler));
+  this._Popcorn.listen("IriSP.PlayerWidget.MouseOut", IriSP.wrap(this, this.mouseOutHandler));
+};
+
+/* update the slider and the position marker as time passes */
+IriSP.SliderWidget.prototype.sliderUpdater = function() {
+  if(this.draggingOngoing || this._disableUpdate)
+    return;
+  
+  var time = this._Popcorn.currentTime();
+
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var percents = time / duration;
+  
+  /* we do these complicated calculations to center exactly
+     the position Marker */
+
+  var divWidth = this.selector.width();
+  var pixels = Math.floor(this.selector.width() * percents);
+  var positionMarker_width = this.positionMarker.width();
+  var correction = (positionMarker_width / 2);
+
+  /* check that we don't leave the left side */
+  var newPos = pixels - correction;
+  if (newPos <= 0)
+    newPos = 0;
+  
+  /* check that we don't leave the right side */
+  var rightEdgePos = pixels + 1 * correction;
+
+  if (rightEdgePos >= divWidth)
+    newPos = divWidth - 1 * correction - 1;
+  
+  this.sliderForeground.css("width", pixels + "px");
+  this.positionMarker.css("left", newPos + "px");
+
+};
+
+IriSP.SliderWidget.prototype.backgroundClickHandler = function(event) {
+  /* this piece of code is a little bit convoluted - here's how it works :
+     we want to handle clicks on the progress bar and convert those to seeks in the media.
+     However, jquery only gives us a global position, and we want a number of pixels relative
+     to our container div, so we get the parent position, and compute an offset to this position,
+     and finally compute the progress ratio in the media.
+     Finally we multiply this ratio with the duration to get the correct time
+  */
+
+  var parentOffset = this.sliderBackground.parent().offset();
+  var width = this.sliderBackground.width();
+  var relX = event.pageX - parentOffset.left;
+
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var newTime = ((relX / width) * duration).toFixed(2);
+
+  this._Popcorn.currentTime(newTime);
+};
+
+/* same function as the previous one, except that it handles clicks
+   on the foreground element */
+IriSP.SliderWidget.prototype.foregroundClickHandler = function(event) {
+  var parentOffset = this.sliderForeground.parent().offset();
+  var width = this.sliderBackground.width();
+  var relX = event.pageX - parentOffset.left;
+
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var newTime = ((relX / width) * duration).toFixed(2);
+
+  this._Popcorn.currentTime(newTime);
+};
+
+/* handles mouse over the slider */
+IriSP.SliderWidget.prototype.mouseOverHandler = function(event) {
+  
+  if (this.timeOutId !== null) {
+    window.clearTimeout(this.timeOutId);
+  }
+ 
+  this.sliderMaximized = true;
+
+  this.sliderBackground.animate({"height": "9px"}, 100);
+  this.sliderForeground.animate({"height": "9px"}, 100);
+  this.positionMarker.animate({"height": "9px", "width": "9px"}, 100);
+  //this.positionMarker.css("margin-top", "-4px");
+  
+//  this.selector.removeClass("Ldt-SliderMinimized");
+//  this.selector.addClass("Ldt-SliderMaximized");
+};
+
+/* handles when the mouse leaves the slider */
+IriSP.SliderWidget.prototype.mouseOutHandler = function(event) {
+
+  this.timeOutId = window.setTimeout(IriSP.wrap(this, this.minimizeOnTimeout),
+                                     IriSP.widgetsDefaults.SliderWidget.minimize_period);
+};
+
+IriSP.SliderWidget.prototype.minimizeOnTimeout = function(event) {
+  this.sliderBackground.animate({"height": "5px"}, 100);
+  this.sliderForeground.animate({"height": "5px"}, 100);
+  this.positionMarker.animate({"height": "5px", "width": "5px"}, 100);
+  this.positionMarker.css("margin-top", "0px");
+  this.sliderMinimized = true;
+  
+//  this.selector.removeClass("Ldt-SliderMaximized");
+//  this.selector.addClass("Ldt-SliderMinimized");
+
+};
+
+// called when the user starts dragging the position indicator
+IriSP.SliderWidget.prototype.positionMarkerDraggingStartedHandler = function(event, ui) {  
+  this.draggingOngoing = true;
+};
+
+IriSP.SliderWidget.prototype.positionMarkerDraggedHandler = function(event, ui) {   
+  this._disableUpdate = true; // disable slider position updates while dragging is ongoing.
+  window.setTimeout(IriSP.wrap(this, function() { this._disableUpdate = false; }), 500);
+
+  var parentOffset = this.sliderForeground.parent().offset();
+  var width = this.sliderBackground.width();
+  var relX = event.pageX - parentOffset.left;
+
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var newTime = ((relX / width) * duration).toFixed(2);
+
+  this._Popcorn.currentTime(newTime);
+  
+  this.draggingOngoing = false;
+};
+
+/** @class The constructor for the sparkline widget */
+IriSP.SparklineWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+
+  this._oldAnnotation = null;
+  this._results = [];
+};
+
+
+IriSP.SparklineWidget.prototype = new IriSP.Widget();
+
+IriSP.SparklineWidget.prototype.clear = function() {
+
+};
+
+/** draw the sparkline using jquery sparkline */
+IriSP.SparklineWidget.prototype.draw = function() {
+  var templ = Mustache.to_html(IriSP.SparklineWidget_template, {width: this.width, height: this.height});
+  /** this widget uses three divs -
+    the first is the sparkline, which is generated by jquery sparkline,
+    the second is an overlay div to display the progression in the video,
+    and the third is a div to react to clicks
+  */
+  
+  var views = this._serializer._data.views;
+  var stat_view;
+  if (!IriSP.null_or_undefined(views)) {
+    
+    var i;
+    for (i = 0; i < views.length; i++) {
+      var view = views[i];
+      if (view.id === "stat") {
+          stat_view = view;
+          break;
+      }
+    }
+  }
+  
+  // If we've found the correct view, feed the directly the data from the view
+  // to jquery sparkline. Otherwise, compute it ourselves.
+  if (!IriSP.null_or_undefined(stat_view)) {
+    console.log("sparklinewidget : using stats embedded in the json");
+    var results = stat_view.meta.stat.split(",");      
+  } else {
+    console.log("sparklinewidget : computing stats ourselves");
+    var num_columns = (this.selector.width()) / IriSP.widgetsDefaults["SparklineWidget"].column_width;
+    var duration = +this._serializer.currentMedia().meta["dc:duration"];
+    var time_step = duration / num_columns; /* the time interval between two columns */
+    var results = [];
+    var i = 0; /* the index in the loop */  
+
+    /* this algorithm makes one assumption : that the array is sorted 
+       (it's done for us by the JSONSerializer). We go through the array 
+       and count how many comments fall within a peculiar time piece.
+       As i is preserved between each iteration, it's O(n).
+    */
+    
+    for(var j = 0; j < num_columns && i < this._serializer._data.annotations.length; j++) {    
+      var count = 0;
+      var annotation_begin = +(this._serializer._data.annotations[i].begin);
+      
+      while(annotation_begin >= j * time_step && annotation_begin <= (j + 1) * time_step ) {
+        count++;
+        i++;
+        if (i >= this._serializer._data.annotations.length)
+          break;
+          
+        annotation_begin = +(this._serializer._data.annotations[i].begin);
+        
+      }
+      
+      results.push(count);
+    }
+  }
+  
+  // save the results in an array so that we can re-use them when a new annotation
+  // is added.
+  this._results = results;
+  
+  this.selector.append(templ);
+  this.selector.find(".Ldt-sparkLine").css("background", "#c7c8cc");
+  this.selector.find(".Ldt-sparkLine").sparkline(results, {lineColor: "#7492b4", fillColor: "#aeaeb8",
+                                                           spotColor: "#b70056",
+                                                           width: this.width, height: this.height});
+  this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler));
+  this._Popcorn.listen("IriSP.createAnnotationWidget.addedAnnotation", IriSP.wrap(this, this.handleNewAnnotation));
+  
+  IriSP.jQuery(".Ldt-sparkLineClickOverlay").click(IriSP.wrap(this, this.clickHandler));  
+};
+
+/** react to a timeupdate event */
+IriSP.SparklineWidget.prototype.timeUpdateHandler = function() {
+  var currentTime = this._Popcorn.currentTime();  
+  var duration = +this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var proportion = ((currentTime / duration) * 100).toFixed(4);
+  
+  IriSP.jQuery(".Ldt-sparkLinePositionMarker").css("width", proportion + "%");                                    
+}
+
+/** handle clicks on the widget */
+IriSP.SparklineWidget.prototype.clickHandler = function(event) {
+  /* this piece of code is a little bit convoluted - here's how it works :
+     we want to handle clicks on the progress bar and convert those to seeks in the media.
+     However, jquery only gives us a global position, and we want a number of pixels relative
+     to our container div, so we get the parent position, and compute an offset to this position,
+     and finally compute the progress ratio in the media.
+     Finally we multiply this ratio with the duration to get the correct time
+  */
+
+  var parentOffset = this.selector.offset();
+  var width = this.selector.width();
+  var relX = event.pageX - parentOffset.left;
+
+  var duration = this._serializer.currentMedia().meta["dc:duration"] / 1000;
+  var newTime = ((relX / width) * duration).toFixed(2);
+    
+  this._Popcorn.trigger("IriSP.SparklineWidget.clicked", newTime);
+  this._Popcorn.currentTime(newTime);                                 
+};
+
+/** react when a new annotation is added */
+IriSP.SparklineWidget.prototype.handleNewAnnotation = function(annotation) {
+  var num_columns = this._results.length;
+  var duration = +this._serializer.currentMedia().meta["dc:duration"];
+  var time_step = Math.round(duration / num_columns); /* the time interval between two columns */
+  var begin = +annotation.begin;
+  var end = +annotation.end;
+  
+  /* increment all the values between the beginning and the end of the annotation */
+  var index_begin = Math.floor(begin / time_step);
+  var index_end = Math.floor(end / time_step);
+  
+  for (var i = index_begin; i < Math.min(index_end, this._results.length); i++) {
+    this._results[i]++;
+  }
+  
+  this.selector.find(".Ldt-sparkLine").sparkline(this._results, {lineColor: "#7492b4", fillColor: "#aeaeb8",
+                                                           spotColor: "#b70056",
+                                                           width: this.width, height: this.height});
+};IriSP.StackGraphWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+}
+
+IriSP.StackGraphWidget.prototype = new IriSP.Widget();
+
+IriSP.StackGraphWidget.prototype.draw = function() {
+    var _defaultTags = [
+            {
+                "keywords" : [ "++" ],
+                "description" : "positif",
+                "color" : "#1D973D"
+            },
+            {
+                "keywords" : [ "--" ],
+                "description" : "negatif",
+                "color" : "#CE0A15"
+            },
+            {
+                "keywords" : [ "==" ],
+                "description" : "reference",
+                "color" : "#C5A62D"  
+            },
+            {
+                "keywords" : [ "??" ],
+                "description" : "question",
+                "color" : "#036AAE"
+            }
+        ],
+        _defaultDefColor = "#585858";
+    this.height =  this._config.height || 50;
+    this.width = this.selector.width();
+    this.isStreamGraph = this._config.streamgraph || false;
+    this.sliceCount = this._config.slices || ~~(this.width/(this.isStreamGraph ? 20 : 5));
+    this.tagconf = (this._config.tags
+        ? this._config.tags
+        : _defaultTags);
+    IriSP._(this.tagconf).each(function(_a) {
+        _a.regexp = new RegExp(_a.keywords.map(function(_k) {
+            return _k.replace(/([\W])/gm,'\\$1');
+        }).join("|"),"im")
+    });
+    this.defaultcolorconf = (this._config.defaultcolor
+        ? this._config.defaultcolor
+        : _defaultDefColor);
+    this.paper = new Raphael(this.selector[0], this.width, this.height);
+    this.groups = [];
+    this.duration = this._serializer.currentMedia().meta["dc:duration"];
+    
+    var _annotationType = this._serializer.getTweets(),
+        _sliceDuration = ~~ ( this.duration / this.sliceCount),
+        _annotations = this._serializer._data.annotations,
+        _groupedAnnotations = IriSP._.range(this.sliceCount).map(function(_i) {
+            return _annotations.filter(function(_a){
+                return (_a.begin <= (1 + _i) * _sliceDuration) && (_a.end >= _i * _sliceDuration)
+            });
+        }),
+        _max = IriSP._(_groupedAnnotations).max(function(_g) {
+            return _g.length
+        }).length,
+        _scale = this.height / _max,
+        _width = this.width / this.sliceCount
+        _showTitle = !this._config.excludeTitle,
+        _showDescription = !this._config.excludeDescription;
+    
+    
+    var _paths = this.tagconf.map(function() {
+        return [];
+    });
+    _paths.push([]);
+    
+    for (var i = 0; i < this.sliceCount; i++) {
+        var _group = _groupedAnnotations[i];
+        if (_group) {
+            var _vol = this.tagconf.map(function() {
+                return 0;
+            });
+            for (var j = 0; j < _group.length; j++){
+           var _txt = (_showTitle ? _group[j].content.title : '') + ' ' + (_showDescription ? _group[j].content.description : '')
+                var _tags = this.tagconf.map(function(_tag) {
+                        return (_txt.search(_tag.regexp) == -1 ? 0 : 1)
+                    }),
+                    _nbtags = _tags.reduce(function(_a,_b) {
+                        return _a + _b;
+                    }, 0);
+                if (_nbtags) {
+                    IriSP._(_tags).each(function(_v, _k) {
+                        _vol[_k] += (_v / _nbtags);
+                    });
+                }
+            }
+            var _nbtags = _vol.reduce(function(_a,_b) {
+                    return _a + _b;
+                }, 0),
+                _nbneutre = _group.length - _nbtags,
+                _h = _nbneutre * _scale,
+                _base = this.height - _h;
+            if (!this.isStreamGraph) {
+                this.paper.rect(i * _width, _base, _width - 1, _h ).attr({
+                    "stroke" : "none",
+                    "fill" : this.defaultcolorconf
+                });
+            }
+           _paths[0].push(_base);
+            for (var j = 0; j < this.tagconf.length; j++) {
+                _h = _vol[j] * _scale;
+                _base = _base - _h;
+                if (!this.isStreamGraph) {
+                    this.paper.rect(i * _width, _base, _width - 1, _h ).attr({
+                        "stroke" : "none",
+                        "fill" : this.tagconf[j].color
+                    });
+                }
+                _paths[j+1].push(_base);
+            }
+            this.groups.push(_vol.map(function(_v) {
+                return _v / _group.length;
+            }))
+        } else {
+            for (var j = 0; j < _paths.length; j++) {
+                _paths[j].push(this.height);
+            }
+            this.groups.push(this.tagconf.map(function() {
+                return 0;
+            }));
+        }
+    }
+    
+    if (this.isStreamGraph) {
+        for (var j = _paths.length - 1; j >= 0; j--) {
+            var _d = _paths[j].reduce(function(_memo, _v, _k) {
+               return _memo + ( _k
+                   ? 'C' + (_k * _width) + ' ' + _paths[j][_k - 1] + ' ' + (_k * _width) + ' ' + _v + ' ' + ((_k + .5) * _width) + ' ' + _v
+                   : 'M0 ' + _v + 'L' + (.5*_width) + ' ' + _v )
+            },'') + 'L' + this.width + ' ' + _paths[j][_paths[j].length - 1] + 'L' + this.width + ' ' + this.height + 'L0 ' + this.height;
+            this.paper.path(_d).attr({
+                "stroke" : "none",
+                "fill" : (j ? this.tagconf[j-1].color : this.defaultcolorconf)
+            });
+        }
+    }
+    this.rectangleFocus = this.paper.rect(0,0,_width,this.height)
+        .attr({
+            "stroke" : "none",
+            "fill" : "#ff00ff",
+            "opacity" : 0
+        })
+    this.rectangleProgress = this.paper.rect(0,0,0,this.height)
+        .attr({
+            "stroke" : "none",
+            "fill" : "#808080",
+            "opacity" : .3
+        });
+    this.ligneProgress = this.paper.path("M0 0L0 "+this.height).attr({"stroke":"#ff00ff", "line-width" : 2})
+    
+    this._Popcorn.listen("timeupdate", IriSP.wrap(this, this.timeUpdateHandler));
+    var _this = this;
+    this.selector
+        .click(IriSP.wrap(this, this.clickHandler))
+        .mousemove(function(event) {
+            _this.updateTooltip(event);
+            
+            // Also tell the world where the mouse is hovering.
+            var relX = event.pageX - _this.selector.offset().left;
+            var duration = _this._serializer.currentMedia().meta["dc:duration"];
+            var Time = ((relX / _this.width) * duration).toFixed(2);
+            _this._Popcorn.trigger("IriSP.StackGraphWidget.mouseOver", Time);
+
+        })
+        .mouseout(function() {
+            _this.TooltipWidget.hide();
+            _this.rectangleFocus.attr({
+                "opacity" : 0
+            })
+        })
+}
+
+IriSP.StackGraphWidget.prototype.timeUpdateHandler = function() {
+    var _currentTime = this._Popcorn.currentTime(),
+        _x = (1000 * _currentTime / this.duration) * this.width;
+    this.rectangleProgress.attr({
+        "width" : _x
+    });
+    this.ligneProgress.attr({
+        "path" : "M" + _x + " 0L" + _x + " " + this.height
+    })
+}
+
+IriSP.StackGraphWidget.prototype.clickHandler = function(event) {
+  /* Ctrl-C Ctrl-V'ed from another widget
+  */
+
+  var relX = event.pageX - this.selector.offset().left;
+  var newTime = ((relX / this.width) * this.duration/1000).toFixed(2);
+  this._Popcorn.trigger("IriSP.StackGraphWidget.clicked", newTime);
+  this._Popcorn.currentTime(newTime);                                 
+};
+
+IriSP.StackGraphWidget.prototype.updateTooltip = function(event) {
+    var _segment = ~~(this.sliceCount * (event.pageX - this.selector.offset().left)/this.width),
+        _valeurs = this.groups[_segment],
+        _width = this.width / this.sliceCount,
+        _html = '<ul style="list-style: none; margin: 0; padding: 0;">' + this.tagconf.map(function(_tag, _i) {
+            return '<li style="clear: both;"><span style="float: left; width: 10px; height: 10px; margin: 2px; background: '
+                + _tag.color
+                + ';"></span>'
+                + ~~(100 * _valeurs[_i])
+                + '% de '
+                + _tag.description
+                + '</li>';
+        }).join('') + '</ul>';
+    this.TooltipWidget._shown = false; // Vraiment, on ne peut pas ouvrir le widget s'il n'est pas encore ouvert ?
+    this.TooltipWidget.show('','',event.pageX - 105, event.pageY - 160);
+    this.TooltipWidget.selector.find(".tip").html(_html);
+    this.rectangleFocus.attr({
+        "x" : _segment * _width,
+        "opacity" : .4
+    })
+}
+
+IriSP.TagCloudWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+}
+
+IriSP.TagCloudWidget.prototype = new IriSP.Widget();
+
+IriSP.TagCloudWidget.prototype.draw = function() {
+    
+    var _stopwords = [
+            'aussi', 'and', 'avec', 'aux', 'car', 'cette', 'comme', 'dans', 'donc', 'des', 'elle', 'est',
+            'être', 'eux', 'fait', 'ici', 'ils', 'les', 'leur', 'leurs', 'mais', 'mes', 'même', 'mon', 'notre',
+            'non', 'nos', 'nous', 'ont', 'par', 'pas', 'peu', 'pour', 'que', 'qui', 'ses' ,'son', 'sont', 'sur',
+            'tes', 'très', 'the', 'ton', 'tous', 'tout', 'une', 'votre', 'vos', 'vous'
+        ],
+        _regexpword = /[^\s\.&;,'"!\?\d\(\)\+\[\]\\\…\-«»:\/]{3,}/g,
+        _words = {},
+        _showTitle = !this._config.excludeTitle,
+        _showDescription = !this._config.excludeDescription,
+        _tagCount = this._config.tagCount || 30;
+    
+    IriSP._(this._serializer._data.annotations).each(function(_annotation) {
+       if (_annotation.content && _annotation.content.description) {
+           var _txt = (_showTitle ? _annotation.content.title : '') + ' ' + (_showDescription ? _annotation.content.description : '')
+           IriSP._(_txt.toLowerCase().match(_regexpword)).each(function(_mot) {
+               if (_stopwords.indexOf(_mot) == -1) {
+                   _words[_mot] = 1 + (_words[_mot] || 0);
+               }
+           })
+       } 
+    });
+    
+    _words = IriSP._(_words)
+        .chain()
+        .map(function(_v, _k) {
+            return {
+                "word" : _k,
+                "count" : _v
+            }
+        })
+        .filter(function(_v) {
+            return _v.count > 2;
+        })
+        .sortBy(function(_v) {
+            return - _v.count;
+        })
+        .first(_tagCount)
+        .value();
+    var _max = _words[0].count,
+        _min = Math.min(_words[_words.length - 1].count, _max - 1),
+        _scale = 16 / Math.sqrt(_max - _min),
+        _this = this,
+        _html = '<ul>'
+            + IriSP._(_words)
+                .chain()
+                .shuffle()
+                .map(function(_word) {
+                    var _size = 10 + _scale * Math.sqrt(_word.count - _min);
+                    return '<li style="font-size:'
+                        + _size
+                        + 'px;">'
+                        + _word.word
+                        + '</li>'
+                })
+                .value()
+                .join("")
+            + '</ul>';
+    this.selector
+        .addClass("Ldt-TagCloud")
+        .html(_html);
+    this.selector.find("li").click(function() {
+        var _txt = this.textContent.replace(/(^[\s]+|[\s]+$)/g,'');
+        _this._Popcorn.trigger("IriSP.search.triggeredSearch", _txt);
+    });
+    this._Popcorn.listen("IriSP.search", IriSP.wrap(this, function(searchString) {
+        var _rgxp = new RegExp("(" + searchString.replace(/(\W)/g,'\\$1') + ")","gi");
+        this.selector.find("li").each(function(_i, _e) {
+            _e.innerHTML = searchString.length ?
+                _e.textContent.replace(_rgxp,'<span class="Ldt-TagCloud-actif">$1</span>')
+                : _e.textContent;
+        });
+    }));
+    this._Popcorn.listen("IriSP.search.closed", IriSP.wrap(this, this.endsearch));
+    this._Popcorn.listen("IriSP.search.cleared", IriSP.wrap(this, this.endsearch));
+}
+
+IriSP.TagCloudWidget.prototype.endsearch = function() {
+    this.selector.find("li").each(function(_i, _e) {
+        _e.innerHTML = _e.textContent;
+    });
+}
+/* this widget displays a small tooltip */
+IriSP.TooltipWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+  this._shown = false;
+  this._displayedText = "";
+  this._hideTimeout = -1;
+};
+
+
+IriSP.TooltipWidget.prototype = new IriSP.Widget();
+
+IriSP.TooltipWidget.prototype.draw = function() {
+  var templ = Mustache.to_html(IriSP.tooltipWidget_template);
+  // position the widget absolutely relative to document.
+  this.selector.css("position", "static");
+  this.selector.append(templ);
+  this.hide();
+
+};
+
+IriSP.TooltipWidget.prototype.clear = function() {
+	this.selector.find(".tiptext").html("");
+};
+
+IriSP.TooltipWidget.prototype.show = function(text, color, x, y) {
+
+  if (this._displayedText == text && this._shown)
+    return;
+
+  this.selector.find(".tipcolor").css("background-color", color);
+  this._displayedText = text;
+	this.selector.find(".tiptext").html(text);
+  
+  if (x < 0)
+    x = 0;
+  if (y < 0)
+    y = 0;
+  
+  this.selector.find(".tip").css("left", x).css("top", y);
+  this.selector.find(".tip").show();
+  this._shown = true;
+};
+
+IriSP.TooltipWidget.prototype.hide = function() {                                                   
+  this.selector.find(".tip").hide();
+  this._shown = false;  
+};/* a widget that displays tweet - used in conjunction with the polemicWidget */
+
+IriSP.TweetsWidget = function(Popcorn, config, Serializer) {
+  IriSP.Widget.call(this, Popcorn, config, Serializer);
+
+  this._displayingTweet = false;
+  this._timeoutId = undefined; 
+  this._hidden = false; /* hidden means that the createAnnotationWidget is shown */
+};
+
+
+IriSP.TweetsWidget.prototype = new IriSP.Widget();
+
+
+IriSP.TweetsWidget.prototype.drawTweet = function(annotation) {
+    if (this._hidden)
+      return;
+    
+    var title = IriSP.formatTweet(annotation.content.title);
+    var img = annotation.content.img.src;
+    if (typeof(img) === "undefined" || img === "" || img === "None") {
+      img = IriSP.widgetsDefaults.TweetsWidget.default_profile_picture;
+    }
+
+    var imageMarkup = IriSP.templToHTML("<img src='{{src}}' alt='user image'></img>", 
+                                       {src : img});
+    
+    if (typeof(annotation.meta["dc:source"].content) !== "undefined") {
+      var tweetContents = JSON.parse(annotation.meta["dc:source"].content);
+      var creator = tweetContents.user.screen_name;
+      var real_name = tweetContents.user.name;
+
+      imageMarkup = IriSP.templToHTML("<a href='http://twitter.com/{{creator}}'><img src='{{src}}' alt='user image'></img></a>", 
+                                       {src : img, creator: creator});
+            
+      var formatted_date = new Date(tweetContents.created_at).toLocaleDateString();
+      title = IriSP.templToHTML("<a class='Ldt-tweet_userHandle' href='http://twitter.com/{{creator}}'>@{{creator}}</a> - " + 
+                                "<div class='Ldt-tweet_realName'>{{real_name}}</div>" +
+                                "<div class='Ldt-tweet_tweetContents'>{{{ contents }}}</div>" +
+                                "<div class='Ldt-tweet_date'>{{ date }}</div>", 
+                                {creator: creator, real_name: real_name, contents : title, date : formatted_date});
+
+      this.selector.find(".Ldt-TweetReply").attr("href", "http://twitter.com/home?status=@" + creator + ":%20");
+
+
+      var rtText = Mustache.to_html("http://twitter.com/home?status=RT @{{creator}}: {{text}}",
+                                    {creator: creator, text: IriSP.encodeURI(annotation.content.title)});
+      this.selector.find(".Ldt-Retweet").attr("href", rtText);
+    }
+
+    this.selector.find(".Ldt-tweetContents").html(title);
+    this.selector.find(".Ldt-tweetAvatar").html(imageMarkup);
+    this.selector.show("blind", 250); 
+};
+
+IriSP.TweetsWidget.prototype.displayTweet = function(annotation) {
+  if (this._displayingTweet === false) {
+    this._displayingTweet = true;
+  } else {
+    window.clearTimeout(this._timeoutId);
+  }
+
+  this.drawTweet(annotation);
+
+  var time = this._Popcorn.currentTime();  
+  this._timeoutId = window.setTimeout(IriSP.wrap(this, this.clearPanel), IriSP.widgetsDefaults.TweetsWidget.tweet_display_period);
+};
+
+
+IriSP.TweetsWidget.prototype.clearPanel = function() {  
+    this._displayingTweet = false;
+    this._timeoutId = undefined;
+    this.closePanel();
+    
+};
+
+IriSP.TweetsWidget.prototype.closePanel = function() {
+    if (this._timeoutId != undefined) {
+      /* we're called from the "close window" link */
+      /* cancel the timeout */
+      window.clearTimeout(this._timeoutId);
+      this._timeoutId = null;
+    }
+    
+    this.selector.hide("blind", 400);
+    
+};
+
+/* cancel the timeout if the user clicks on the keep panel open button */
+IriSP.TweetsWidget.prototype.keepPanel = function() {
+    if (this._timeoutId != undefined) {
+      /* we're called from the "close window" link */
+      /* cancel the timeout */
+      window.clearTimeout(this._timeoutId);
+      this._timeoutId = null;
+    }
+};
+
+IriSP.TweetsWidget.prototype.draw = function() {
+  var _this = this;
+  
+  var tweetMarkup = IriSP.templToHTML(IriSP.tweetWidget_template, {"share_template" : IriSP.share_template});
+  this.selector.append(tweetMarkup);
+  this.selector.hide();
+  this.selector.find(".Ldt-tweetWidgetMinimize").click(IriSP.wrap(this, this.closePanel));
+  this.selector.find(".Ldt-tweetWidgetKeepOpen").click(IriSP.wrap(this, this.keepPanel));
+  
+  this._Popcorn.listen("IriSP.PolemicTweet.click", IriSP.wrap(this, this.PolemicTweetClickHandler));
+  this._Popcorn.listen("IriSP.PlayerWidget.AnnotateButton.clicked", 
+                        IriSP.wrap(this, this.handleAnnotateSignal));  
+};
+
+IriSP.TweetsWidget.prototype.PolemicTweetClickHandler = function(tweet_id) {  
+  var index, annotation;
+  for (index in this._serializer._data.annotations) {
+    annotation = this._serializer._data.annotations[index];
+    
+    if (annotation.id === tweet_id)
+      break;
+  }
+    
+  if (annotation.id !== tweet_id)
+      /* we haven't found it */
+      return;
+  
+  this.displayTweet(annotation);
+  return;
+};
+
+/** handle clicks on the annotate button by hiding/showing itself */
+IriSP.TweetsWidget.prototype.handleAnnotateSignal = function() {
+  if (this._hidden == false) {
+    this.selector.hide();
+    this._hidden = true;
+  } else {
+    if (this._displayingTweet !== false)
+      this.selector.show();
+      
+    
+    this._hidden = false;
+  }
+};/** @class This class implement a serializer for the JSON-Cinelab format
+    @params DataLoader a dataloader reference
+    @url the url from which to get our cinelab
+ */
+IriSP.JSONSerializer = function(DataLoader, url) {
+  IriSP.Serializer.call(this, DataLoader, url);
+};
+
+IriSP.JSONSerializer.prototype = new IriSP.Serializer();
+
+/** serialize data */
+IriSP.JSONSerializer.prototype.serialize = function(data) {
+  return JSON.stringify(data);
+};
+
+/** deserialize data */
+IriSP.JSONSerializer.prototype.deserialize = function(data) {
+  return JSON.parse(data);
+};
+
+/** load JSON-cinelab data and also sort the annotations by start time
+    @param callback function to call when the data is ready.
+ */
+IriSP.JSONSerializer.prototype.sync = function(callback) {
+  /* we don't have to do much because jQuery handles json for us */
+
+  var self = this;
+
+  var fn = function(data) {
+	  //TODO: seems taht data can be null here
+	  if (data !== null) {
+		  self._data = data;  
+	      if (typeof(self._data["annotations"]) === "undefined" ||
+	          self._data["annotations"] === null)
+	          self._data["annotations"] = [];
+	      
+	      // sort the data too       
+	      self._data["annotations"].sort(function(a, b) 
+	          { var a_begin = +a.begin;
+	            var b_begin = +b.begin;
+	            return a_begin - b_begin;
+	          });
+	  }     
+      callback(data);      
+  };
+  
+  this._DataLoader.get(this._url, fn);
+};
+
+/** @return the metadata about the media being read FIXME: always return the first media. */
+IriSP.JSONSerializer.prototype.currentMedia = function() {  
+  return this._data.medias[0]; /* FIXME: don't hardcode it */
+};
+
+/** searches for an annotation which matches title, description and keyword 
+   "" matches any field. 
+   Note: it ignores tweets.
+   @return a list of matching ids.
+*/    
+IriSP.JSONSerializer.prototype.searchAnnotations = function(title, description, keyword) {
+    /* we can have many types of annotations. We want search to only look for regular segments */
+    /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either
+       null or undefined.
+    */
+    var view;
+
+    if (typeof(this._data.views) !== "undefined" && this._data.views !== null)
+       view = this._data.views[0];
+
+    var searchViewType = "";
+
+    if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
+            searchViewType = view.annotation_types[0];
+    }
+
+    var filterfn = function(annotation) {
+      if( searchViewType  != "" && 
+          typeof(annotation.meta) !== "undefined" && 
+          typeof(annotation.meta["id-ref"]) !== "undefined" &&
+          annotation.meta["id-ref"] !== searchViewType) {
+        return true; // don't pass
+      } else {
+          return false;
+      }
+    };
+
+    return this.searchAnnotationsFilter(title, description, keyword, filterfn);
+
+};
+
+/* only look for tweets */
+IriSP.JSONSerializer.prototype.searchTweets = function(title, description, keyword) {
+    /* we can have many types of annotations. We want search to only look for regular segments */
+    /* the next two lines are a bit verbose because for some test data, _serializer.data.view is either
+       null or undefined.
+    */
+    
+    var searchViewType = this.getTweets();
+    if (typeof(searchViewType) === "undefined") {
+      var view;
+      
+      if (typeof(this._data.views) !== "undefined" && this._data.views !== null)
+         view = this._data.views[0];    
+
+      if(typeof(view) !== "undefined" && typeof(view.annotation_types) !== "undefined" && view.annotation_types.length > 1) {
+              searchViewType = view.annotation_types[0];
+      }
+    }
+    var filterfn = function(annotation) {
+      if( searchViewType  != "" && 
+          typeof(annotation.meta) !== "undefined" && 
+          typeof(annotation.meta["id-ref"]) !== "undefined" &&
+          annotation.meta["id-ref"] === searchViewType) {
+        return false; // pass
+      } else {
+          return true;
+      }
+    };
+
+    return this.searchAnnotationsFilter(title, description, keyword, filterfn);
+
+};
+
+/**
+  search an annotation according to its title, description and keyword
+  @param filter a function to filter the results with. Used to select between annotation types.
+ */    
+IriSP.JSONSerializer.prototype.searchAnnotationsFilter = function(title, description, keyword, filter) {
+
+    var rTitle;
+    var rDescription;
+    var rKeyword;
+    /* match anything if given the empty string */
+    if (title == "")
+      title = ".*";
+    if (description == "")
+      description = ".*";
+    if (keyword == "")
+      keyword = ".*";
+    
+    rTitle = new RegExp(title, "i");  
+    rDescription = new RegExp(description, "i");  
+    rKeyword = new RegExp(keyword, "i");  
+    
+    var ret_array = [];
+    
+    var i;
+    for (i in this._data.annotations) {
+      var annotation = this._data.annotations[i];
+      
+      /* filter the annotations whose type is not the one we want */
+      if (filter(annotation)) {
+          continue;
+      }
+      
+      if (rTitle.test(annotation.content.title) && 
+          rDescription.test(annotation.content.description)) {
+          /* FIXME : implement keyword support */
+          ret_array.push(annotation);
+      }
+    }
+    
+    return ret_array;
+};
+
+/** breaks a string in words and searches each of these words. Returns an array
+   of objects with the id of the annotation and its number of occurences.
+   
+   @param searchString a string of words.
+   FIXME: optimize ? seems to be n^2 in the worst case.
+*/
+IriSP.JSONSerializer.prototype.searchOccurences = function(searchString) {
+  var ret = { };
+  var keywords = searchString.split(/\s+/);
+  
+  for (var i in keywords) {
+    var keyword = keywords[i];
+    
+    // search this keyword in descriptions and title
+    var found_annotations = []
+    found_annotations = found_annotations.concat(this.searchAnnotations(keyword, "", ""));
+    found_annotations = found_annotations.concat(this.searchAnnotations("", keyword, ""));
+    
+    for (var j in found_annotations) {
+      var current_annotation = found_annotations[j];
+      
+      if (!ret.hasOwnProperty(current_annotation.id)) {
+        ret[current_annotation.id] = 1;
+      } else {
+        ret[current_annotation.id] += 1;
+      }
+      
+    }
+
+  };
+  
+  return ret;
+};
+
+/** breaks a string in words and searches each of these words. Returns an array
+   of objects with the id of the annotation and its number of occurences.
+   
+   FIXME: optimize ? seems to be n^2 in the worst case.
+*/
+IriSP.JSONSerializer.prototype.searchTweetsOccurences = function(searchString) {
+  var ret = { };
+  var keywords = searchString.split(/\s+/);
+  
+  for (var i in keywords) {
+    var keyword = keywords[i];
+    
+    // search this keyword in descriptions and title
+    var found_annotations = []
+    found_annotations = found_annotations.concat(this.searchTweets(keyword, "", ""));
+    found_annotations = found_annotations.concat(this.searchTweets("", keyword, ""));
+    
+    for (var j in found_annotations) {
+      var current_annotation = found_annotations[j];
+      
+      if (!ret.hasOwnProperty(current_annotation.id)) {
+        ret[current_annotation.id] = 1;
+      } else {
+        ret[current_annotation.id] += 1;
+      }
+      
+    }
+
+  };
+  
+  return ret;
+};
+
+/** returns all the annotations that are displayable at the moment 
+   NB: only takes account the first type of annotations - ignores tweets 
+   currentTime is in seconds.
+   
+   @param currentTime the time at which we search.
+   @param (optional) the if of the type of the annotations we want to get.
+ */
+
+IriSP.JSONSerializer.prototype.currentAnnotations = function(currentTime, id) {
+  var view;
+  var currentTimeMs = 1000 * currentTime;
+
+  if (typeof(id) === "undefined") {
+      var legal_ids = this.getNonTweetIds();
+  } else {
+      legal_ids = [id];
+  }
+  
+  var ret_array = [];
+  
+  var i;
+  
+  for (i in this._data.annotations) {
+    var annotation = this._data.annotations[i];
+    
+    if (IriSP.underscore.include(legal_ids, annotation.meta["id-ref"]) && 
+        annotation.begin <= currentTimeMs &&
+        annotation.end >= currentTimeMs)
+          ret_array.push(annotation);
+  }
+ 
+  if (ret_array == []) {
+    console.log("ret_array empty, ", legal_ids);
+  }
+  
+  return ret_array;
+};
+
+/** return the current chapitre
+    @param currentTime the current time, in seconds.
+*/
+IriSP.JSONSerializer.prototype.currentChapitre = function(currentTime) {
+  return this.currentAnnotations(currentTime, this.getChapitrage())[0];
+};
+
+/** returns a list of ids of tweet lines (aka: groups in cinelab) */
+IriSP.JSONSerializer.prototype.getTweetIds = function() {
+  if (IriSP.null_or_undefined(this._data.lists) || IriSP.null_or_undefined(this._data.lists) ||
+      IriSP.null_or_undefined(this._data.views) || IriSP.null_or_undefined(this._data.views[0]))
+    return [];
+
+  
+  /* Get the displayable types
+     We've got to jump through a few hoops because the json sometimes defines
+     fields with underscores and sometimes with dashes
+  */
+  var annotation_types = this._data.views[0]["annotation_types"];
+  if (IriSP.null_or_undefined(annotation_types)) {
+    annotation_types = this._data.views[0]["annotation-types"];
+    if (IriSP.null_or_undefined(annotation_types)) {
+      console.log("neither view.annotation_types nor view.annotation-types are defined");      
+      return;
+    }
+  }
+
+  var available_types = this._data["annotation_types"];    
+  if (IriSP.null_or_undefined(available_types)) {
+    available_types = this._data["annotation-types"];
+    if (IriSP.null_or_undefined(available_types)) {
+      console.log("neither annotation_types nor annotation-types are defined");      
+      return;
+    }
+  }
+  
+  var potential_types = [];
+  
+  // Get the list of types which contain "Tw" in their content
+  for (var i = 0; i < available_types.length; i++) {
+    if (/Tw/i.test(available_types[i]["dc:title"])) {
+      potential_types.push(available_types[i].id);
+    }
+  }
+  
+  // Get the intersection of both.
+  var tweetsId = IriSP.underscore.intersection(annotation_types, potential_types);
+  
+  return tweetsId;
+};
+
+/** this function returns a list of lines which are not tweet lines */
+IriSP.JSONSerializer.prototype.getNonTweetIds = function() {
+  if (IriSP.null_or_undefined(this._data.lists) || IriSP.null_or_undefined(this._data.lists) ||
+      IriSP.null_or_undefined(this._data.views) || IriSP.null_or_undefined(this._data.views[0]))
+    return [];
+
+  /* Get the displayable types
+     We've got to jump through a few hoops because the json sometimes defines
+     fields with underscores and sometimes with dashes
+  */
+  var annotation_types = this._data.views[0]["annotation_types"];
+  if (IriSP.null_or_undefined(annotation_types)) {
+    annotation_types = this._data.views[0]["annotation-types"];
+    if (IriSP.null_or_undefined(annotation_types)) {
+      console.log("neither view.annotation_types nor view.annotation-types are defined");      
+      return;
+    }
+  }
+
+  var available_types = this._data["annotation_types"];    
+  if (IriSP.null_or_undefined(available_types)) {
+    available_types = this._data["annotation-types"];
+    if (IriSP.null_or_undefined(available_types)) {
+      console.log("neither annotation_types nor annotation-types are defined");      
+      return;
+    }
+  }
+
+  var potential_types = [];
+  
+  // Get the list of types which do not contain "Tw" in their content
+  for (var i = 0; i < available_types.length; i++) {
+    if (!(/Tw/i.test(available_types[i]["dc:title"]))) {
+      potential_types.push(available_types[i].id);
+    }
+  }
+
+  // Get the intersection of both.
+  var nonTweetsId = IriSP.underscore.intersection(annotation_types, potential_types);
+  
+  return nonTweetsId;
+  
+};
+
+/** return the id of the ligne de temps which contains name
+    @param name of the ligne de temps
+*/
+IriSP.JSONSerializer.prototype.getId = function(name) {
+  if (IriSP.null_or_undefined(this._data["annotation-types"]))
+    return;
+
+  name = name.toUpperCase();
+  var e;  
+  e = IriSP.underscore.find(this._data["annotation-types"], 
+                                  function(entry) { 
+                                    if (IriSP.null_or_undefined(entry["dc:title"]))
+                                      return false;
+                                    
+                                    return (entry["dc:title"].toUpperCase().indexOf(name) !== -1) });
+  
+  if (typeof(e) === "undefined")
+    return;
+    
+  var id = e.id;
+
+  return id;
+};
+
+/** return the list of id's of the ligne de temps which contains name
+    @param name of the ligne de temps
+*/
+IriSP.JSONSerializer.prototype.getIds = function(name) {
+  if (IriSP.null_or_undefined(this._data["annotation-types"]))
+    return;
+
+  name = name.toUpperCase();
+  var e = [];  
+  e = IriSP.underscore.filter(this._data["annotation-types"], 
+                                  function(entry) { return (entry["dc:title"].toUpperCase().indexOf(name) !== -1) });
+  return IriSP.underscore.pluck(e, "id");  
+};
+
+/** return the id of the ligne de temps named "Chapitrage" */
+IriSP.JSONSerializer.prototype.getChapitrage = function() {
+  var val = this.getId("Chapitrage");
+  if (typeof(val) === "undefined")
+    val = this.getId("Chapter");   
+  if (typeof(val) === "undefined")
+    val = this.getId("Chapit");
+  if (typeof(val) === "undefined")
+    val = this.getId("Chap");
+    
+  return val;
+};
+
+/** return the id of the ligne de temps named "Tweets" */
+IriSP.JSONSerializer.prototype.getTweets = function() {
+  var val = this.getId("Tweets");
+  if (typeof(val) === "undefined")
+    val = this.getId("Tweet");
+  if (typeof(val) === "undefined")
+    val = this.getId("Twitter");
+  if (typeof(val) === "undefined")
+    val = this.getId("twit");
+  if (typeof(val) === "undefined")
+    val = this.getId("Polemic");
+  
+  return val;
+};
+
+/** return the id of the ligne de temps named "Contributions" */
+IriSP.JSONSerializer.prototype.getContributions = function() {
+  var val = this.getId("Contribution");
+  if (typeof(val) === "undefined")
+    val = this.getId("Particip");   
+  if (typeof(val) === "undefined")
+    val = this.getId("Contr");
+  if (typeof(val) === "undefined")
+    val = this.getId("Publ");
+    
+  return val;
+};
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/jquery.sparkline.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,1271 @@
+/**
+*
+* jquery.sparkline.js
+*
+* v1.6
+* (c) Splunk, Inc 
+* Contact: Gareth Watts (gareth@splunk.com)
+* http://omnipotent.net/jquery.sparkline/
+*
+* Generates inline sparkline charts from data supplied either to the method
+* or inline in HTML
+* 
+* Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag
+* (Firefox 2.0+, Safari, Opera, etc)
+*
+* License: New BSD License
+* 
+* Copyright (c) 2010, Splunk Inc.
+* All rights reserved.
+* 
+* Redistribution and use in source and binary forms, with or without modification, 
+* are permitted provided that the following conditions are met:
+* 
+*     * Redistributions of source code must retain the above copyright notice, 
+*       this list of conditions and the following disclaimer.
+*     * Redistributions in binary form must reproduce the above copyright notice, 
+*       this list of conditions and the following disclaimer in the documentation 
+*       and/or other materials provided with the distribution.
+*     * Neither the name of Splunk Inc nor the names of its contributors may 
+*       be used to endorse or promote products derived from this software without 
+*       specific prior written permission.
+* 
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
+* SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
+* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+* 
+*
+* Usage: 
+*  $(selector).sparkline(values, options)
+*
+* If values is undefined or set to 'html' then the data values are read from the specified tag:
+*   <p>Sparkline: <span class="sparkline">1,4,6,6,8,5,3,5</span></p>
+*   $('.sparkline').sparkline();
+* There must be no spaces in the enclosed data set
+*
+* Otherwise values must be an array of numbers or null values
+*    <p>Sparkline: <span id="sparkline1">This text replaced if the browser is compatible</span></p>
+*    $('#sparkline1').sparkline([1,4,6,6,8,5,3,5])
+*    $('#sparkline2').sparkline([1,4,6,null,null,5,3,5])
+*
+* Values can also be specified in an HTML comment, or as a values attribute:
+*    <p>Sparkline: <span class="sparkline"><!--1,4,6,6,8,5,3,5 --></span></p>
+*    <p>Sparkline: <span class="sparkline" values="1,4,6,6,8,5,3,5"></span></p>
+*    $('.sparkline').sparkline();
+*
+* For line charts, x values can also be specified:
+*   <p>Sparkline: <span class="sparkline">1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5</span></p>
+*    $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ])
+*
+* By default, options should be passed in as teh second argument to the sparkline function:
+*   $('.sparkline').sparkline([1,2,3,4], {type: 'bar'})
+*
+* Options can also be set by passing them on the tag itself.  This feature is disabled by default though
+* as there's a slight performance overhead:
+*   $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true})
+*   <p>Sparkline: <span class="sparkline" sparkType="bar" sparkBarColor="red">loading</span></p>
+* Prefix all options supplied as tag attribute with "spark" (configurable by setting tagOptionPrefix)
+*
+* Supported options:
+*   lineColor - Color of the line used for the chart
+*   fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart
+*   width - Width of the chart - Defaults to 3 times the number of values in pixels
+*   height - Height of the chart - Defaults to the height of the containing element
+*   chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied
+*   chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied
+*   chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax
+*   chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied
+*   chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied
+*   composite - If true then don't erase any existing chart attached to the tag, but draw
+*           another chart over the top - Note that width and height are ignored if an
+*           existing chart is detected.
+*   tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values'
+*   enableTagOptions - Whether to check tags for sparkline options 
+*   tagOptionPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark'
+*
+* There are 7 types of sparkline, selected by supplying a "type" option of 'line' (default),
+* 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box'
+*    line - Line chart.  Options:
+*       spotColor - Set to '' to not end each line in a circular spot
+*       minSpotColor - If set, color of spot at minimum value
+*       maxSpotColor - If set, color of spot at maximum value
+*       spotRadius - Radius in pixels
+*       lineWidth - Width of line in pixels
+*       normalRangeMin 
+*       normalRangeMax - If set draws a filled horizontal bar between these two values marking the "normal"
+*                      or expected range of values
+*       normalRangeColor - Color to use for the above bar
+*       drawNormalOnTop - Draw the normal range above the chart fill color if true
+*       defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart
+*
+*   bar - Bar chart.  Options:
+*       barColor - Color of bars for postive values
+*       negBarColor - Color of bars for negative values
+*       zeroColor - Color of bars with zero values
+*       nullColor - Color of bars with null values - Defaults to omitting the bar entirely
+*       barWidth - Width of bars in pixels
+*       colorMap - Optional mappnig of values to colors to override the *BarColor values above
+*                  can be an Array of values to control the color of individual bars
+*       barSpacing - Gap between bars in pixels
+*       zeroAxis - Centers the y-axis around zero if true
+*
+*   tristate - Charts values of win (>0), lose (<0) or draw (=0)
+*       posBarColor - Color of win values
+*       negBarColor - Color of lose values
+*       zeroBarColor - Color of draw values
+*       barWidth - Width of bars in pixels
+*       barSpacing - Gap between bars in pixels
+*       colorMap - Optional mappnig of values to colors to override the *BarColor values above
+*                  can be an Array of values to control the color of individual bars
+*
+*   discrete - Options:
+*       lineHeight - Height of each line in pixels - Defaults to 30% of the graph height
+*       thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor
+*       thresholdColor
+*
+*   bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ...
+*       options:
+*       targetColor - The color of the vertical target marker
+*       targetWidth - The width of the target marker in pixels
+*       performanceColor - The color of the performance measure horizontal bar
+*       rangeColors - Colors to use for each qualitative range background color
+*
+*   pie - Pie chart. Options:
+*       sliceColors - An array of colors to use for pie slices
+*       offset - Angle in degrees to offset the first slice - Try -90 or +90
+*
+*   box - Box plot. Options:
+*       raw - Set to true to supply pre-computed plot points as values
+*             values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier
+*             When set to false you can supply any number of values and the box plot will
+*             be computed for you.  Default is false.
+*       showOutliers - Set to true (default) to display outliers as circles
+*       outlierIRQ - Interquartile range used to determine outliers.  Default 1.5
+*       boxLineColor - Outline color of the box
+*       boxFillColor - Fill color for the box
+*       whiskerColor - Line color used for whiskers
+*       outlierLineColor - Outline color of outlier circles
+*       outlierFillColor - Fill color of the outlier circles
+*       spotRadius - Radius of outlier circles
+*       medianColor - Line color of the median line
+*       target - Draw a target cross hair at the supplied value (default undefined)
+*      
+*   
+*       
+*   Examples:
+*   $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false });
+*   $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 });
+*   $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }):
+*   $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' });
+*   $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' });
+*   $('#pie').sparkline([1,1,2], { type:'pie' });
+*/
+
+
+(function($) {
+
+    /*
+     * Default configuration settings
+     */
+    var defaults = {
+        // Settings common to most/all chart types
+        common: {
+            type : 'line',
+            lineColor : '#00f',
+            fillColor : '#cdf',
+            defaultPixelsPerValue : 3,
+            width : 'auto', 
+            height : 'auto',
+            composite : false,
+            tagValuesAttribute: 'values',
+            tagOptionsPrefix: 'spark',
+            enableTagOptions: false
+        },
+        // Defaults for line charts
+        line: {
+            spotColor : '#f80',
+            spotRadius : 1.5,
+            minSpotColor : '#f80',
+            maxSpotColor : '#f80',
+            lineWidth: 1, 
+            normalRangeMin : undefined,
+            normalRangeMax : undefined,
+            normalRangeColor : '#ccc',
+            drawNormalOnTop: false,
+            chartRangeMin : undefined,
+            chartRangeMax : undefined,
+            chartRangeMinX : undefined,
+            chartRangeMaxX : undefined
+        },
+        // Defaults for bar charts
+        bar: {
+            barColor : '#00f',
+            negBarColor : '#f44',
+            zeroColor: undefined,
+            nullColor: undefined,
+            zeroAxis : undefined,
+            barWidth : 4,
+            barSpacing : 1,
+            chartRangeMax: undefined,
+            chartRangeMin: undefined,
+            chartRangeClip: false,
+            colorMap : undefined
+        },
+        // Defaults for tristate charts
+        tristate: {
+            barWidth : 4,
+            barSpacing : 1,
+            posBarColor: '#6f6',
+            negBarColor : '#f44',
+            zeroBarColor : '#999',
+            colorMap : {}
+        },
+        // Defaults for discrete charts
+        discrete: {
+            lineHeight: 'auto',
+            thresholdColor: undefined,
+            thresholdValue : 0,
+            chartRangeMax: undefined,
+            chartRangeMin: undefined,
+            chartRangeClip: false
+        },
+        // Defaults for bullet charts
+        bullet: {
+            targetColor : 'red',
+            targetWidth : 3, // width of the target bar in pixels
+            performanceColor : 'blue',
+            rangeColors : ['#D3DAFE', '#A8B6FF', '#7F94FF' ],
+            base : undefined // set this to a number to change the base start number
+        },
+        // Defaults for pie charts
+        pie: {
+            sliceColors : ['#f00', '#0f0', '#00f']
+        },
+        // Defaults for box plots
+        box: {
+            raw: false,
+            boxLineColor: 'black',
+            boxFillColor: '#cdf',
+            whiskerColor: 'black',
+            outlierLineColor: '#333',
+            outlierFillColor: 'white',
+            medianColor: 'red',
+            showOutliers: true,
+            outlierIQR: 1.5,
+            spotRadius: 1.5,
+            target: undefined,
+            targetColor: '#4a2',
+            chartRangeMax: undefined,
+            chartRangeMin: undefined
+        }
+    };
+
+    // Provide a cross-browser interface to a few simple drawing primitives
+    var VCanvas_base, VCanvas_canvas, VCanvas_vml;
+    $.fn.simpledraw = function(width, height, use_existing) {
+        if (use_existing && this[0].VCanvas) {
+            return this[0].VCanvas;
+        }
+        if (width === undefined) { 
+            width=$(this).innerWidth();
+        }
+        if (height === undefined) {
+            height=$(this).innerHeight();
+        }
+        if ($.browser.hasCanvas) {
+            return new VCanvas_canvas(width, height, this);
+        } else if ($.browser.msie) {
+            return new VCanvas_vml(width, height, this);
+        } else {
+            return false;
+        }
+    };
+
+    var pending = [];
+
+
+    $.fn.sparkline = function(uservalues, userOptions) {
+        return this.each(function() {
+            var options = new $.fn.sparkline.options(this, userOptions);
+            var render = function() {
+                var values, width, height;
+                if (uservalues==='html' || uservalues===undefined) {
+                    var vals = this.getAttribute(options.get('tagValuesAttribute'));
+                    if (vals===undefined || vals===null) {
+                        vals = $(this).html();
+                    }
+                    values = vals.replace(/(^\s*<!--)|(-->\s*$)|\s+/g, '').split(',');
+                } else {
+                    values = uservalues;
+                }
+
+                width = options.get('width')=='auto' ? values.length*options.get('defaultPixelsPerValue') : options.get('width');
+                if (options.get('height') == 'auto') {
+                    if (!options.get('composite') || !this.VCanvas) {
+                        // must be a better way to get the line height
+                        var tmp = document.createElement('span');
+                        tmp.innerHTML = 'a';
+                        $(this).html(tmp);
+                        height = $(tmp).innerHeight();
+                        $(tmp).remove();
+                    }
+                } else {
+                    height = options.get('height');
+                }
+
+                $.fn.sparkline[options.get('type')].call(this, values, options, width, height);
+            };
+            // jQuery 1.3.0 completely changed the meaning of :hidden :-/
+            if (($(this).html() && $(this).is(':hidden')) || ($.fn.jquery < "1.3.0" && $(this).parents().is(':hidden')) || !$(this).parents('body').length) {
+                pending.push([this, render]);
+            } else {
+                render.call(this);
+            }
+        });
+    };
+
+    $.fn.sparkline.defaults = defaults;
+
+
+    $.sparkline_display_visible = function() {
+        for (var i=pending.length-1; i>=0; i--) {
+            var el = pending[i][0];
+            if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
+                pending[i][1].call(el);
+                pending.splice(i, 1);
+            }
+        }
+    };
+
+
+    /**
+     * User option handler
+     */
+    var UNSET_OPTION = {};
+    var normalizeValue = function(val) {
+        switch(val) {
+            case 'undefined':
+                val = undefined;
+                break;
+            case 'null':
+                val = null;
+                break;
+            case 'true':
+                val = true;
+                break;
+            case 'false':
+                val = false;
+                break;
+            default:
+                var nf = parseFloat(val);
+                if (val == nf) {
+                    val = nf;
+                }
+        }
+        return val;
+    };
+    $.fn.sparkline.options = function(tag, userOptions) {
+        var extendedOptions;
+        this.userOptions = userOptions = userOptions || {};
+        this.tag = tag;
+        this.tagValCache = {};
+        var defaults = $.fn.sparkline.defaults;
+        var base = defaults.common;
+        this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);
+            
+        var tagOptionType = this.getTagSetting('type');
+        if (tagOptionType === UNSET_OPTION) {
+            extendedOptions = defaults[userOptions.type || base.type];
+        } else {
+            extendedOptions = defaults[tagOptionType];
+        }
+        this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);
+    };
+
+
+    $.fn.sparkline.options.prototype.getTagSetting = function(key) {
+        var val, i, prefix = this.tagOptionsPrefix;
+        if (prefix === false || prefix === undefined) {
+            return UNSET_OPTION;
+        }
+        if (this.tagValCache.hasOwnProperty(key)) {
+            val = this.tagValCache.key;
+        } else {
+            val = this.tag.getAttribute(prefix + key);
+            if (val === undefined || val === null) {
+                val = UNSET_OPTION;
+            } else if (val.substr(0, 1) == '[') {
+                val = val.substr(1, val.length-2).split(',');
+                for(i=val.length; i--;) {
+                    val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, ''));
+                }
+            } else if (val.substr(0, 1) == '{') {
+                var pairs= val.substr(1, val.length-2).split(',');
+                val = {};
+                for(i=pairs.length; i--;) {
+                    var keyval = pairs[i].split(':', 2);
+                    val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, ''));
+                }
+            } else {
+                val = normalizeValue(val);
+            }
+            this.tagValCache.key = val;
+        }
+        return val; 
+    };
+
+    $.fn.sparkline.options.prototype.get = function(key) {
+        var tagOption = this.getTagSetting(key);
+        if (tagOption !== UNSET_OPTION) {
+            return tagOption;
+        }
+        return this.mergedOptions[key];
+    };
+
+
+    /**
+     * Line charts
+     */
+    $.fn.sparkline.line = function(values, options, width, height) {
+        var xvalues = [], yvalues = [], yminmax = [];
+        for (var i=0; i<values.length; i++) {
+            var val = values[i];
+            var isstr = typeof(values[i])=='string';
+            var isarray = typeof(values[i])=='object' && values[i] instanceof Array;
+            var sp = isstr && values[i].split(':');
+            if (isstr && sp.length == 2) { // x:y
+                xvalues.push(Number(sp[0]));
+                yvalues.push(Number(sp[1]));
+                yminmax.push(Number(sp[1]));
+            } else if (isarray) {
+                xvalues.push(val[0]);
+                yvalues.push(val[1]);
+                yminmax.push(val[1]);
+            } else {
+                xvalues.push(i);
+                if (values[i]===null || values[i]=='null') {
+                    yvalues.push(null);
+                } else {
+                    yvalues.push(Number(val));
+                    yminmax.push(Number(val));
+                }
+            }
+        }
+        if (options.get('xvalues')) {
+            xvalues = options.get('xvalues');
+        }
+
+        var maxy = Math.max.apply(Math, yminmax);
+        var maxyval = maxy;
+        var miny = Math.min.apply(Math, yminmax);
+        var minyval = miny;
+
+        var maxx = Math.max.apply(Math, xvalues);
+        var minx = Math.min.apply(Math, xvalues);
+
+        var normalRangeMin = options.get('normalRangeMin');
+        var normalRangeMax = options.get('normalRangeMax');
+
+        if (normalRangeMin!==undefined) {
+            if (normalRangeMin<miny) {
+                miny = normalRangeMin;
+            }
+            if (normalRangeMax>maxy) {
+                maxy = normalRangeMax;
+            }
+        }
+        if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') ||  options.get('chartRangeMin')<miny)) {
+            miny = options.get('chartRangeMin');
+        }
+        if (options.get('chartRangeMax')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMax')>maxy)) {
+            maxy = options.get('chartRangeMax');
+        }
+        if (options.get('chartRangeMinX')!==undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX')<minx)) {
+            minx = options.get('chartRangeMinX');
+        }
+        if (options.get('chartRangeMaxX')!==undefined && (options.get('chartRangeClipX') || options.get('chartRangeMaxX')>maxx)) {
+            maxx = options.get('chartRangeMaxX');
+        }
+        var rangex = maxx-minx === 0 ? 1 : maxx-minx;
+        var rangey = maxy-miny === 0 ? 1 : maxy-miny;
+        var vl = yvalues.length-1;
+
+        if (vl<1) {
+            this.innerHTML = '';
+            return;
+        }
+
+        var target = $(this).simpledraw(width, height, options.get('composite'));
+        if (target) {
+            var canvas_width = target.pixel_width;
+            var canvas_height = target.pixel_height;
+            var canvas_top = 0;
+            var canvas_left = 0;
+
+            var spotRadius = options.get('spotRadius');
+            if (spotRadius && (canvas_width < (spotRadius*4) || canvas_height < (spotRadius*4))) {
+                spotRadius = 0;
+            }
+            if (spotRadius) {
+                // adjust the canvas size as required so that spots will fit
+                if (options.get('minSpotColor') || (options.get('spotColor') && yvalues[vl]==miny)) {
+                    canvas_height -= Math.ceil(spotRadius);
+                }
+                if (options.get('maxSpotColor') || (options.get('spotColor') && yvalues[vl]==maxy)) {
+                    canvas_height -= Math.ceil(spotRadius);
+                    canvas_top += Math.ceil(spotRadius);
+                }
+                if (options.get('minSpotColor') || options.get('maxSpotColor') && (yvalues[0]==miny || yvalues[0]==maxy)) {
+                    canvas_left += Math.ceil(spotRadius);
+                    canvas_width -= Math.ceil(spotRadius);
+                }
+                if (options.get('spotColor') || (options.get('minSpotColor') || options.get('maxSpotColor') && (yvalues[vl]==miny||yvalues[vl]==maxy))) {
+                    canvas_width -= Math.ceil(spotRadius);
+                }
+            }
+
+
+            canvas_height--;
+
+            var drawNormalRange = function() {
+                if (normalRangeMin!==undefined) {
+                    var ytop = canvas_top+Math.round(canvas_height-(canvas_height*((normalRangeMax-miny)/rangey)));
+                    var height = Math.round((canvas_height*(normalRangeMax-normalRangeMin))/rangey);
+                    target.drawRect(canvas_left, ytop, canvas_width, height, undefined, options.get('normalRangeColor'));
+                }
+            };
+
+            if (!options.get('drawNormalOnTop')) {
+                drawNormalRange();
+            }
+
+            var path = [];
+            var paths = [path];
+            var x, y, vlen=yvalues.length;
+            for(i=0; i<vlen; i++) {
+                x=xvalues[i];
+                y=yvalues[i];
+                if (y===null) {
+                    if (i) {
+                        if (yvalues[i-1]!==null) {
+                            path = [];
+                            paths.push(path);
+                        }
+                    }
+                } else {
+                    if (y < miny) {
+                        y=miny;
+                    }
+                    if (y > maxy) {
+                        y=maxy;
+                    }
+                    if (!path.length) {
+                        // previous value was null
+                        path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+canvas_height]);
+                    }
+                    path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+Math.round(canvas_height-(canvas_height*((y-miny)/rangey)))]);
+                }
+            }
+            var lineshapes = [];
+            var fillshapes = [];
+            var plen=paths.length;
+            for(i=0; i<plen; i++) {
+                path = paths[i];
+                if (!path.length) {
+                    continue; // last value was null
+                }
+                if (options.get('fillColor')) {
+                    path.push([path[path.length-1][0], canvas_top+canvas_height-1]);
+                    fillshapes.push(path.slice(0));
+                    path.pop();
+                }
+                // if there's only a single point in this path, then we want to display it as a vertical line
+                // which means we keep path[0]  as is
+                if (path.length>2) {
+                    // else we want the first value 
+                    path[0] = [ path[0][0], path[1][1] ];
+                }
+                lineshapes.push(path);
+            }
+
+            // draw the fill first, then optionally the normal range, then the line on top of that
+            plen = fillshapes.length;
+            for(i=0; i<plen; i++) {
+                target.drawShape(fillshapes[i], undefined, options.get('fillColor'));
+            }
+
+            if (options.get('drawNormalOnTop')) {
+                drawNormalRange();
+            }
+
+            plen = lineshapes.length;
+            for(i=0; i<plen; i++) {
+                target.drawShape(lineshapes[i], options.get('lineColor'), undefined, options.get('lineWidth'));
+            }
+                
+            if (spotRadius && options.get('spotColor')) {
+                target.drawCircle(canvas_left+Math.round(xvalues[xvalues.length-1]*(canvas_width/rangex)),  canvas_top+Math.round(canvas_height-(canvas_height*((yvalues[vl]-miny)/rangey))), spotRadius, undefined, options.get('spotColor'));
+            }
+            if (maxy!=minyval) {
+                if (spotRadius && options.get('minSpotColor')) {
+                    x = xvalues[$.inArray(minyval, yvalues)];
+                    target.drawCircle(canvas_left+Math.round((x-minx)*(canvas_width/rangex)),  canvas_top+Math.round(canvas_height-(canvas_height*((minyval-miny)/rangey))), spotRadius, undefined, options.get('minSpotColor'));
+                }
+                if (spotRadius && options.get('maxSpotColor')) {
+                    x = xvalues[$.inArray(maxyval, yvalues)];
+                    target.drawCircle(canvas_left+Math.round((x-minx)*(canvas_width/rangex)),  canvas_top+Math.round(canvas_height-(canvas_height*((maxyval-miny)/rangey))), spotRadius, undefined, options.get('maxSpotColor'));
+                }
+            }
+
+        } else {
+            // Remove the tag contents if sparklines aren't supported
+            this.innerHTML = '';
+        }
+    };
+
+    
+    /** 
+     * Bar charts
+     */
+    $.fn.sparkline.bar = function(values, options, width, height) {
+        width = (values.length * options.get('barWidth')) + ((values.length-1) * options.get('barSpacing'));
+        var num_values = [];
+        for(var i=0, vlen=values.length; i<vlen; i++) {
+            if (values[i]=='null' || values[i]===null) {
+                values[i] = null;
+            } else {
+                values[i] = Number(values[i]);
+                num_values.push(Number(values[i]));
+            }
+        }
+        var max = Math.max.apply(Math, num_values),
+            min = Math.min.apply(Math, num_values);
+        if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')<min)) {
+            min = options.get('chartRangeMin');
+        }
+        if (options.get('chartRangeMax')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMax')>max)) {
+            max = options.get('chartRangeMax');
+        }
+        var zeroAxis = options.get('zeroAxis');
+        if (zeroAxis === undefined) {
+            zeroAxis = min<0;
+        }
+        var range = max-min === 0 ? 1 : max-min;
+
+        var colorMapByIndex, colorMapByValue;
+        if ($.isArray(options.get('colorMap'))) {
+            colorMapByIndex = options.get('colorMap');
+            colorMapByValue = null;
+        } else {
+            colorMapByIndex = null;
+            colorMapByValue = options.get('colorMap');
+        }
+
+        var target = $(this).simpledraw(width, height, options.get('composite'));
+        if (target) {
+            var color,
+                canvas_height = target.pixel_height,
+                yzero = min<0 && zeroAxis ? canvas_height-Math.round(canvas_height * (Math.abs(min)/range))-1 : canvas_height-1;
+
+            for(i=values.length; i--;) {
+                var x = i*(options.get('barWidth')+options.get('barSpacing')),
+                    y, 
+                    val = values[i];
+                if (val===null) {
+                    if (options.get('nullColor')) {
+                        color = options.get('nullColor');
+                        val = (zeroAxis && min<0) ? 0 : min;
+                        height = 1;
+                        y = (zeroAxis && min<0) ? yzero : canvas_height - height;
+                    } else {
+                        continue;
+                    }
+                } else {
+                    if (val < min) {
+                        val=min;
+                    }
+                    if (val > max) {
+                        val=max;
+                    }
+                    color = (val < 0) ? options.get('negBarColor') : options.get('barColor');
+                    if (zeroAxis && min<0) {
+                        height = Math.round(canvas_height*((Math.abs(val)/range)))+1;
+                        y = (val < 0) ? yzero : yzero-height;
+                    } else {
+                        height = Math.round(canvas_height*((val-min)/range))+1;
+                        y = canvas_height-height;
+                    }
+                    if (val===0 && options.get('zeroColor')!==undefined) {
+                        color = options.get('zeroColor');
+                    }
+                    if (colorMapByValue && colorMapByValue[val]) {
+                        color = colorMapByValue[val];
+                    } else if (colorMapByIndex && colorMapByIndex.length>i) {
+                        color = colorMapByIndex[i];
+                    }
+                    if (color===null) {
+                        continue;
+                    }
+                }
+                target.drawRect(x, y, options.get('barWidth')-1, height-1, color, color);
+            }
+        } else {
+            // Remove the tag contents if sparklines aren't supported
+            this.innerHTML = '';
+        }
+    };
+
+
+    /**
+     * Tristate charts
+     */
+    $.fn.sparkline.tristate = function(values, options, width, height) {
+        values = $.map(values, Number);
+        width = (values.length * options.get('barWidth')) + ((values.length-1) * options.get('barSpacing'));
+
+        var colorMapByIndex, colorMapByValue;
+        if ($.isArray(options.get('colorMap'))) {
+            colorMapByIndex = options.get('colorMap');
+            colorMapByValue = null;
+        } else {
+            colorMapByIndex = null;
+            colorMapByValue = options.get('colorMap');
+        }
+
+        var target = $(this).simpledraw(width, height, options.get('composite'));
+        if (target) {
+            var canvas_height = target.pixel_height,
+                half_height = Math.round(canvas_height/2);
+
+            for(var i=values.length; i--;) {
+                var x = i*(options.get('barWidth')+options.get('barSpacing')),
+                    y, color;
+                if (values[i] < 0) {
+                    y = half_height;
+                    height = half_height-1;
+                    color = options.get('negBarColor');
+                } else if (values[i] > 0) {
+                    y = 0;
+                    height = half_height-1;
+                    color = options.get('posBarColor');
+                } else {
+                    y = half_height-1;
+                    height = 2;
+                    color = options.get('zeroBarColor');
+                }
+                if (colorMapByValue && colorMapByValue[values[i]]) {
+                    color = colorMapByValue[values[i]];
+                } else if (colorMapByIndex && colorMapByIndex.length>i) {
+                    color = colorMapByIndex[i];
+                }
+                if (color===null) {
+                    continue;
+                }
+                target.drawRect(x, y, options.get('barWidth')-1, height-1, color, color);
+            }
+        } else {
+            // Remove the tag contents if sparklines aren't supported
+            this.innerHTML = '';
+        }
+    };
+
+
+    /** 
+     * Discrete charts
+     */
+    $.fn.sparkline.discrete = function(values, options, width, height) {
+        values = $.map(values, Number);
+        width = options.get('width')=='auto' ? values.length*2 : width;
+        var interval = Math.floor(width / values.length);
+
+        var target = $(this).simpledraw(width, height, options.get('composite'));
+        if (target) {
+            var canvas_height = target.pixel_height,
+                line_height = options.get('lineHeight') == 'auto' ? Math.round(canvas_height * 0.3) : options.get('lineHeight'),
+                pheight = canvas_height - line_height,
+                min = Math.min.apply(Math, values),
+                max = Math.max.apply(Math, values);
+            if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')<min)) {
+                min = options.get('chartRangeMin');
+            }
+            if (options.get('chartRangeMax')!==undefined && (options.get('chartRangeClip')  || options.get('chartRangeMax')>max)) {
+                max = options.get('chartRangeMax');
+            }
+            var range = max-min;
+
+            for(var i=values.length; i--;) {
+                var val = values[i];
+                if (val < min) {
+                    val=min;
+                }
+                if (val > max) {
+                    val=max;
+                }
+                var x = (i*interval),
+                    ytop = Math.round(pheight-pheight*((val-min)/range));
+                target.drawLine(x, ytop, x, ytop+line_height, (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor'));
+            }
+        }  else {
+            // Remove the tag contents if sparklines aren't supported
+            this.innerHTML = '';
+        }
+                
+    };
+
+
+    /**
+     * Bullet charts
+     */
+    $.fn.sparkline.bullet = function(values, options, width, height) {
+        values = $.map(values, Number);
+        // target, performance, range1, range2, range3
+        
+        width = options.get('width')=='auto' ? '4.0em' : width;
+
+        var target = $(this).simpledraw(width, height, options.get('composite'));
+        if (target && values.length>1) {
+            var canvas_width = target.pixel_width-Math.ceil(options.get('targetWidth')/2),
+                canvas_height = target.pixel_height,
+                min = Math.min.apply(Math, values),
+                max = Math.max.apply(Math, values);
+
+            if (options.get('base') === undefined) {
+                min = min < 0 ? min : 0;
+            } else {
+                min = options.get('base');
+            }
+            var range = max-min;
+
+            // draw range values
+            for(var i=2, vlen=values.length; i<vlen; i++) {
+                var rangeval = values[i],
+                    rangewidth = Math.round(canvas_width*((rangeval-min)/range));
+                target.drawRect(0, 0, rangewidth-1, canvas_height-1, options.get('rangeColors')[i-2], options.get('rangeColors')[i-2]);
+            }
+
+            // draw the performance bar
+            var perfval = values[1],
+                perfwidth = Math.round(canvas_width*((perfval-min)/range));
+            target.drawRect(0, Math.round(canvas_height*0.3), perfwidth-1, Math.round(canvas_height*0.4)-1, options.get('performanceColor'), options.get('performanceColor'));
+
+            // draw the target line
+            var targetval = values[0],
+                x = Math.round(canvas_width*((targetval-min)/range)-(options.get('targetWidth')/2)),
+                targettop = Math.round(canvas_height*0.10),
+                targetheight = canvas_height-(targettop*2);
+            target.drawRect(x, targettop, options.get('targetWidth')-1, targetheight-1, options.get('targetColor'), options.get('targetColor'));
+        }  else {
+            // Remove the tag contents if sparklines aren't supported
+            this.innerHTML = '';
+        }
+    };
+
+
+    /**
+     * Pie charts
+     */
+    $.fn.sparkline.pie = function(values, options, width, height) {
+        values = $.map(values, Number);
+        width = options.get('width')=='auto' ? height : width;
+
+        var target = $(this).simpledraw(width, height, options.get('composite'));
+        if (target && values.length>1) {
+            var canvas_width = target.pixel_width,
+                canvas_height = target.pixel_height,
+                radius = Math.floor(Math.min(canvas_width, canvas_height)/2),
+                total = 0,
+                next = 0,
+                circle = 2*Math.PI;
+
+            for(var i=values.length; i--;) {
+                total += values[i];
+            }
+
+            if (options.get('offset')) {
+                next += (2*Math.PI)*(options.get('offset')/360);
+            }
+            var vlen = values.length;
+            for(i=0; i<vlen; i++) {
+                var start = next;
+                var end = next;
+                if (total > 0) {  // avoid divide by zero
+                    end = next + (circle*(values[i]/total));
+                }
+                target.drawPieSlice(radius, radius, radius, start, end, undefined, options.get('sliceColors')[i % options.get('sliceColors').length]);
+                next = end;
+            }
+        }
+    };
+
+
+    /**
+     * Box plots
+     */
+    var quartile = function(values, q) {
+        if (q==2) {
+            var vl2 = Math.floor(values.length/2);
+            return values.length % 2 ? values[vl2] : (values[vl2]+values[vl2+1])/2;
+        } else {
+            var vl4 = Math.floor(values.length/4);
+            return values.length % 2 ? (values[vl4*q]+values[vl4*q+1])/2 : values[vl4*q];
+        }
+    };
+
+    $.fn.sparkline.box = function(values, options, width, height) {
+        values = $.map(values, Number);
+        width = options.get('width')=='auto' ? '4.0em' : width;
+
+        var minvalue = options.get('chartRangeMin')===undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'),
+            maxvalue = options.get('chartRangeMax')===undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'),
+            target = $(this).simpledraw(width, height, options.get('composite')),
+            vlen = values.length,
+            lwhisker, loutlier, q1, q2, q3, rwhisker, routlier;
+
+        if (target && values.length>1) {
+            var canvas_width = target.pixel_width,
+                canvas_height = target.pixel_height;
+            if (options.get('raw')) {
+                if (options.get('showOutliers') && values.length>5) {
+                    loutlier=values[0]; lwhisker=values[1]; q1=values[2]; q2=values[3]; q3=values[4]; rwhisker=values[5]; routlier=values[6];
+                } else {
+                    lwhisker=values[0]; q1=values[1]; q2=values[2]; q3=values[3]; rwhisker=values[4];
+                }
+            } else {
+                values.sort(function(a, b) { return a-b; });
+                q1 = quartile(values, 1);
+                q2 = quartile(values, 2);
+                q3 = quartile(values, 3);
+                var iqr = q3-q1;
+                if (options.get('showOutliers')) {
+                    lwhisker=undefined; rwhisker=undefined;
+                    for(var i=0; i<vlen; i++) {
+                        if (lwhisker===undefined && values[i] > q1-(iqr*options.get('outlierIQR'))) {
+                            lwhisker = values[i];
+                        }
+                        if (values[i] < q3+(iqr*options.get('outlierIQR'))) {
+                            rwhisker = values[i];
+                        }
+                    }
+                    loutlier = values[0];
+                    routlier = values[vlen-1];
+                } else {
+                    lwhisker = values[0];
+                    rwhisker = values[vlen-1];
+                }
+            }
+
+            var unitsize = canvas_width / (maxvalue-minvalue+1),
+                canvas_left = 0;
+            if (options.get('showOutliers')) {
+                canvas_left = Math.ceil(options.get('spotRadius'));
+                canvas_width -= 2*Math.ceil(options.get('spotRadius'));
+                unitsize = canvas_width / (maxvalue-minvalue+1);
+                if (loutlier < lwhisker) {
+                    target.drawCircle((loutlier-minvalue)*unitsize+canvas_left, canvas_height/2, options.get('spotRadius'), options.get('outlierLineColor'), options.get('outlierFillColor'));
+                }
+                if (routlier > rwhisker) {
+                    target.drawCircle((routlier-minvalue)*unitsize+canvas_left, canvas_height/2, options.get('spotRadius'), options.get('outlierLineColor'), options.get('outlierFillColor'));
+                }
+            }
+
+            // box
+            target.drawRect(
+                Math.round((q1-minvalue)*unitsize+canvas_left),
+                Math.round(canvas_height*0.1),
+                Math.round((q3-q1)*unitsize), 
+                Math.round(canvas_height*0.8), 
+                options.get('boxLineColor'), 
+                options.get('boxFillColor'));
+            // left whisker
+            target.drawLine(
+                Math.round((lwhisker-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height/2), 
+                Math.round((q1-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height/2), 
+                options.get('lineColor'));
+            target.drawLine(
+                Math.round((lwhisker-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height/4), 
+                Math.round((lwhisker-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height-canvas_height/4), 
+                options.get('whiskerColor'));
+            // right whisker
+            target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height/2), 
+                Math.round((q3-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height/2), 
+                options.get('lineColor'));
+            target.drawLine(
+                Math.round((rwhisker-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height/4), 
+                Math.round((rwhisker-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height-canvas_height/4), 
+                options.get('whiskerColor'));
+            // median line
+            target.drawLine(
+                Math.round((q2-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height*0.1),
+                Math.round((q2-minvalue)*unitsize+canvas_left), 
+                Math.round(canvas_height*0.9),
+                options.get('medianColor'));
+            if (options.get('target')) {
+                var size = Math.ceil(options.get('spotRadius'));
+                target.drawLine(
+                    Math.round((options.get('target')-minvalue)*unitsize+canvas_left), 
+                    Math.round((canvas_height/2)-size), 
+                    Math.round((options.get('target')-minvalue)*unitsize+canvas_left), 
+                    Math.round((canvas_height/2)+size), 
+                    options.get('targetColor'));
+                target.drawLine(
+                    Math.round((options.get('target')-minvalue)*unitsize+canvas_left-size), 
+                    Math.round(canvas_height/2), 
+                    Math.round((options.get('target')-minvalue)*unitsize+canvas_left+size), 
+                    Math.round(canvas_height/2), 
+                    options.get('targetColor'));
+            }
+        }  else {
+            // Remove the tag contents if sparklines aren't supported
+            this.innerHTML = '';
+        }
+    };
+
+
+    // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier
+    // This is accessible as $(foo).simpledraw()
+
+    if ($.browser.msie && !document.namespaces.v) {
+        document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');
+    }
+
+    if ($.browser.hasCanvas === undefined) {
+        var t = document.createElement('canvas');
+        $.browser.hasCanvas = t.getContext!==undefined;
+    }
+
+    VCanvas_base = function(width, height, target) {
+    };
+
+    VCanvas_base.prototype = {
+        init : function(width, height, target) {
+            this.width = width;
+            this.height = height;
+            this.target = target;
+            if (target[0]) {
+                target=target[0];
+            }
+            target.VCanvas = this;
+        },
+
+        drawShape : function(path, lineColor, fillColor, lineWidth) {
+            alert('drawShape not implemented');
+        },
+
+        drawLine : function(x1, y1, x2, y2, lineColor, lineWidth) {
+            return this.drawShape([ [x1,y1], [x2,y2] ], lineColor, lineWidth);
+        },
+
+        drawCircle : function(x, y, radius, lineColor, fillColor) {
+            alert('drawCircle not implemented');
+        },
+
+        drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+            alert('drawPieSlice not implemented');
+        },
+
+        drawRect : function(x, y, width, height, lineColor, fillColor) {
+            alert('drawRect not implemented');
+        },
+
+        getElement : function() {
+            return this.canvas;
+        },
+
+        _insert : function(el, target) {
+            $(target).html(el);
+        }
+    };
+
+    VCanvas_canvas = function(width, height, target) {
+        return this.init(width, height, target);
+    };
+
+    VCanvas_canvas.prototype = $.extend(new VCanvas_base(), {
+        _super : VCanvas_base.prototype,
+
+        init : function(width, height, target) {
+            this._super.init(width, height, target);
+            this.canvas = document.createElement('canvas');
+            if (target[0]) {
+                target=target[0];
+            }
+            target.VCanvas = this;
+            $(this.canvas).css({ display:'inline-block', width:width, height:height, verticalAlign:'top' });
+            this._insert(this.canvas, target);
+            this.pixel_height = $(this.canvas).height();
+            this.pixel_width = $(this.canvas).width();
+            this.canvas.width = this.pixel_width;
+            this.canvas.height = this.pixel_height;
+            $(this.canvas).css({width: this.pixel_width, height: this.pixel_height});
+        },
+
+        _getContext : function(lineColor, fillColor, lineWidth) {
+            var context = this.canvas.getContext('2d');
+            if (lineColor !== undefined) {
+                context.strokeStyle = lineColor;
+            }
+            context.lineWidth = lineWidth===undefined ? 1 : lineWidth;
+            if (fillColor !== undefined) {
+                context.fillStyle = fillColor;
+            }
+            return context;
+        },
+
+        drawShape : function(path, lineColor, fillColor, lineWidth) {
+            var context = this._getContext(lineColor, fillColor, lineWidth);
+            context.beginPath();
+            context.moveTo(path[0][0]+0.5, path[0][1]+0.5);
+            for(var i=1, plen=path.length; i<plen; i++) {
+                context.lineTo(path[i][0]+0.5, path[i][1]+0.5); // the 0.5 offset gives us crisp pixel-width lines
+            }
+            if (lineColor !== undefined) {
+                context.stroke();
+            }
+            if (fillColor !== undefined) {
+                context.fill();
+            }
+        },
+
+        drawCircle : function(x, y, radius, lineColor, fillColor) {
+            var context = this._getContext(lineColor, fillColor);
+            context.beginPath();
+            context.arc(x, y, radius, 0, 2*Math.PI, false);
+            if (lineColor !== undefined) {
+                context.stroke();
+            }
+            if (fillColor !== undefined) {
+                context.fill();
+            }
+        }, 
+
+        drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+            var context = this._getContext(lineColor, fillColor);
+            context.beginPath();
+            context.moveTo(x, y);
+            context.arc(x, y, radius, startAngle, endAngle, false);
+            context.lineTo(x, y);
+            context.closePath();
+            if (lineColor !== undefined) {
+                context.stroke();
+            }
+            if (fillColor) {
+                context.fill();
+            }
+        },
+
+        drawRect : function(x, y, width, height, lineColor, fillColor) {
+            return this.drawShape([ [x,y], [x+width, y], [x+width, y+height], [x, y+height], [x, y] ], lineColor, fillColor);
+        }
+        
+    });
+
+    VCanvas_vml = function(width, height, target) {
+        return this.init(width, height, target);
+    };
+
+    VCanvas_vml.prototype = $.extend(new VCanvas_base(), {
+        _super : VCanvas_base.prototype,
+
+        init : function(width, height, target) {
+            this._super.init(width, height, target);
+            if (target[0]) {
+                target=target[0];
+            }
+            target.VCanvas = this;
+            this.canvas = document.createElement('span');
+            $(this.canvas).css({ display:'inline-block', position: 'relative', overflow:'hidden', width:width, height:height, margin:'0px', padding:'0px', verticalAlign: 'top'});
+            this._insert(this.canvas, target);
+            this.pixel_height = $(this.canvas).height();
+            this.pixel_width = $(this.canvas).width();
+            this.canvas.width = this.pixel_width;
+            this.canvas.height = this.pixel_height;
+            var groupel = '<v:group coordorigin="0 0" coordsize="'+this.pixel_width+' '+this.pixel_height+'"' +
+                    ' style="position:absolute;top:0;left:0;width:'+this.pixel_width+'px;height='+this.pixel_height+'px;"></v:group>';
+            this.canvas.insertAdjacentHTML('beforeEnd', groupel);
+            this.group = $(this.canvas).children()[0];
+        },
+
+        drawShape : function(path, lineColor, fillColor, lineWidth) {
+            var vpath = [];
+            for(var i=0, plen=path.length; i<plen; i++) {
+                vpath[i] = ''+(path[i][0])+','+(path[i][1]);
+            }
+            var initial = vpath.splice(0,1);
+            lineWidth = lineWidth === undefined ? 1 : lineWidth;
+            var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="'+lineWidth+'" strokeColor="'+lineColor+'" ';
+            var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" ';
+            var closed = vpath[0] == vpath[vpath.length-1] ? 'x ' : '';
+            var vel = '<v:shape coordorigin="0 0" coordsize="'+this.pixel_width+' '+this.pixel_height+'" ' +
+                 stroke +
+                 fill +
+                ' style="position:absolute;left:0px;top:0px;height:'+this.pixel_height+'px;width:'+this.pixel_width+'px;padding:0px;margin:0px;" ' +
+                ' path="m '+initial+' l '+vpath.join(', ')+' '+closed+'e">' +
+                ' </v:shape>';
+             this.group.insertAdjacentHTML('beforeEnd', vel);
+        },
+
+        drawCircle : function(x, y, radius, lineColor, fillColor) {
+            x -= radius+1;
+            y -= radius+1;
+            var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1" strokeColor="'+lineColor+'" ';
+            var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" ';
+            var vel = '<v:oval ' +
+                stroke +
+                fill +
+                ' style="position:absolute;top:'+y+'px; left:'+x+'px; width:'+(radius*2)+'px; height:'+(radius*2)+'px"></v:oval>';
+            this.group.insertAdjacentHTML('beforeEnd', vel);
+            
+        },
+        
+        drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+            if (startAngle == endAngle) {
+                return;  // VML seems to have problem when start angle equals end angle.
+            }
+            if ((endAngle - startAngle) == (2*Math.PI)) {
+                startAngle = 0.0;  // VML seems to have a problem when drawing a full circle that doesn't start 0
+                endAngle = (2*Math.PI);
+            }
+
+            var startx = x + Math.round(Math.cos(startAngle) * radius);
+            var starty = y + Math.round(Math.sin(startAngle) * radius);
+            var endx = x + Math.round(Math.cos(endAngle) * radius);
+            var endy = y + Math.round(Math.sin(endAngle) * radius);
+
+            // Prevent very small slices from being mistaken as a whole pie
+            if (startx==endx && starty==endy && (endAngle-startAngle) < Math.PI) {
+                return;
+            }
+
+            var vpath = [  x-radius, y-radius, x+radius, y+radius, startx, starty, endx, endy ]; 
+            var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1" strokeColor="'+lineColor+'" ';
+            var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" ';
+            var vel = '<v:shape coordorigin="0 0" coordsize="'+this.pixel_width+' '+this.pixel_height+'" ' +
+                 stroke +
+                 fill +
+                ' style="position:absolute;left:0px;top:0px;height:'+this.pixel_height+'px;width:'+this.pixel_width+'px;padding:0px;margin:0px;" ' +
+                ' path="m '+x+','+y+' wa '+vpath.join(', ')+' x e">' +
+                ' </v:shape>';
+             this.group.insertAdjacentHTML('beforeEnd', vel);
+        },
+
+        drawRect : function(x, y, width, height, lineColor, fillColor) {
+            return this.drawShape( [ [x, y], [x, y+height], [x+width, y+height], [x+width, y], [x, y] ], lineColor, fillColor);
+        }
+    });
+
+})(jQuery);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/jwplayer.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,1 @@
+if(typeof jwplayer=="undefined"){var jwplayer=function(a){if(jwplayer.api){return jwplayer.api.selectPlayer(a)}};var $jw=jwplayer;jwplayer.version="5.7.1896";jwplayer.vid=document.createElement("video");jwplayer.audio=document.createElement("audio");jwplayer.source=document.createElement("source");(function(b){b.utils=function(){};b.utils.typeOf=function(d){var c=typeof d;if(c==="object"){if(d){if(d instanceof Array){c="array"}}else{c="null"}}return c};b.utils.extend=function(){var c=b.utils.extend["arguments"];if(c.length>1){for(var e=1;e<c.length;e++){for(var d in c[e]){c[0][d]=c[e][d]}}return c[0]}return null};b.utils.clone=function(f){var c;var d=b.utils.clone["arguments"];if(d.length==1){switch(b.utils.typeOf(d[0])){case"object":c={};for(var e in d[0]){c[e]=b.utils.clone(d[0][e])}break;case"array":c=[];for(var e in d[0]){c[e]=b.utils.clone(d[0][e])}break;default:return d[0];break}}return c};b.utils.extension=function(c){if(!c){return""}c=c.substring(c.lastIndexOf("/")+1,c.length);c=c.split("?")[0];if(c.lastIndexOf(".")>-1){return c.substr(c.lastIndexOf(".")+1,c.length).toLowerCase()}return};b.utils.html=function(c,d){c.innerHTML=d};b.utils.wrap=function(c,d){if(c.parentNode){c.parentNode.replaceChild(d,c)}d.appendChild(c)};b.utils.ajax=function(g,f,c){var e;if(window.XMLHttpRequest){e=new XMLHttpRequest()}else{e=new ActiveXObject("Microsoft.XMLHTTP")}e.onreadystatechange=function(){if(e.readyState===4){if(e.status===200){if(f){f(e)}}else{if(c){c(g)}}}};try{e.open("GET",g,true);e.send(null)}catch(d){if(c){c(g)}}return e};b.utils.load=function(d,e,c){d.onreadystatechange=function(){if(d.readyState===4){if(d.status===200){if(e){e()}}else{if(c){c()}}}}};b.utils.find=function(d,c){return d.getElementsByTagName(c)};b.utils.append=function(c,d){c.appendChild(d)};b.utils.isIE=function(){return((!+"\v1")||(typeof window.ActiveXObject!="undefined"))};b.utils.isLegacyAndroid=function(){var c=navigator.userAgent.toLowerCase();return(c.match(/android 2.[012]/i)!==null)};b.utils.isIOS=function(d){if(typeof d=="undefined"){d=/iP(hone|ad|od)/i}var c=navigator.userAgent.toLowerCase();return(c.match(d)!==null)};b.utils.isIPad=function(){return b.utils.isIOS(/iPad/i)};b.utils.isIPod=function(){return b.utils.isIOS(/iP(hone|od)/i)};b.utils.getFirstPlaylistItemFromConfig=function(c){var d={};var e;if(c.playlist&&c.playlist.length){e=c.playlist[0]}else{e=c}d.file=e.file;d.levels=e.levels;d.streamer=e.streamer;d.playlistfile=e.playlistfile;d.provider=e.provider;if(!d.provider){if(d.file&&(d.file.toLowerCase().indexOf("youtube.com")>-1||d.file.toLowerCase().indexOf("youtu.be")>-1)){d.provider="youtube"}if(d.streamer&&d.streamer.toLowerCase().indexOf("rtmp://")==0){d.provider="rtmp"}if(e.type){d.provider=e.type.toLowerCase()}}if(d.provider=="audio"){d.provider="sound"}return d};b.utils.getOuterHTML=function(c){if(c.outerHTML){return c.outerHTML}else{try{return new XMLSerializer().serializeToString(c)}catch(d){return""}}};b.utils.setOuterHTML=function(f,e){if(f.outerHTML){f.outerHTML=e}else{var g=document.createElement("div");g.innerHTML=e;var c=document.createRange();c.selectNodeContents(g);var d=c.extractContents();f.parentNode.insertBefore(d,f);f.parentNode.removeChild(f)}};b.utils.hasFlash=function(){if(typeof navigator.plugins!="undefined"&&typeof navigator.plugins["Shockwave Flash"]!="undefined"){return true}if(typeof window.ActiveXObject!="undefined"){try{new ActiveXObject("ShockwaveFlash.ShockwaveFlash");return true}catch(c){}}return false};b.utils.getPluginName=function(c){if(c.lastIndexOf("/")>=0){c=c.substring(c.lastIndexOf("/")+1,c.length)}if(c.lastIndexOf("-")>=0){c=c.substring(0,c.lastIndexOf("-"))}if(c.lastIndexOf(".swf")>=0){c=c.substring(0,c.lastIndexOf(".swf"))}if(c.lastIndexOf(".js")>=0){c=c.substring(0,c.lastIndexOf(".js"))}return c};b.utils.getPluginVersion=function(c){if(c.lastIndexOf("-")>=0){if(c.lastIndexOf(".js")>=0){return c.substring(c.lastIndexOf("-")+1,c.lastIndexOf(".js"))}else{if(c.lastIndexOf(".swf")>=0){return c.substring(c.lastIndexOf("-")+1,c.lastIndexOf(".swf"))}else{return c.substring(c.lastIndexOf("-")+1)}}}return""};b.utils.getAbsolutePath=function(j,h){if(!b.utils.exists(h)){h=document.location.href}if(!b.utils.exists(j)){return undefined}if(a(j)){return j}var k=h.substring(0,h.indexOf("://")+3);var g=h.substring(k.length,h.indexOf("/",k.length+1));var d;if(j.indexOf("/")===0){d=j.split("/")}else{var e=h.split("?")[0];e=e.substring(k.length+g.length+1,e.lastIndexOf("/"));d=e.split("/").concat(j.split("/"))}var c=[];for(var f=0;f<d.length;f++){if(!d[f]||!b.utils.exists(d[f])||d[f]=="."){continue}else{if(d[f]==".."){c.pop()}else{c.push(d[f])}}}return k+g+"/"+c.join("/")};function a(d){if(!b.utils.exists(d)){return}var e=d.indexOf("://");var c=d.indexOf("?");return(e>0&&(c<0||(c>e)))}b.utils.pluginPathType={ABSOLUTE:"ABSOLUTE",RELATIVE:"RELATIVE",CDN:"CDN"};b.utils.getPluginPathType=function(d){if(typeof d!="string"){return}d=d.split("?")[0];var e=d.indexOf("://");if(e>0){return b.utils.pluginPathType.ABSOLUTE}var c=d.indexOf("/");var f=b.utils.extension(d);if(e<0&&c<0&&(!f||!isNaN(f))){return b.utils.pluginPathType.CDN}return b.utils.pluginPathType.RELATIVE};b.utils.mapEmpty=function(c){for(var d in c){return false}return true};b.utils.mapLength=function(d){var c=0;for(var e in d){c++}return c};b.utils.log=function(d,c){if(typeof console!="undefined"&&typeof console.log!="undefined"){if(c){console.log(d,c)}else{console.log(d)}}};b.utils.css=function(d,g,c){if(b.utils.exists(d)){for(var e in g){try{if(typeof g[e]==="undefined"){continue}else{if(typeof g[e]=="number"&&!(e=="zIndex"||e=="opacity")){if(isNaN(g[e])){continue}if(e.match(/color/i)){g[e]="#"+b.utils.strings.pad(g[e].toString(16),6)}else{g[e]=Math.ceil(g[e])+"px"}}}d.style[e]=g[e]}catch(f){}}}};b.utils.isYouTube=function(c){return(c.indexOf("youtube.com")>-1||c.indexOf("youtu.be")>-1)};b.utils.transform=function(c,d){c.style.webkitTransform=d;c.style.MozTransform=d;c.style.OTransform=d};b.utils.stretch=function(h,n,m,f,l,g){if(typeof m=="undefined"||typeof f=="undefined"||typeof l=="undefined"||typeof g=="undefined"){return}var d=m/l;var e=f/g;var k=0;var j=0;n.style.overflow="hidden";b.utils.transform(n,"");var c={};switch(h.toUpperCase()){case b.utils.stretching.NONE:c.width=l;c.height=g;break;case b.utils.stretching.UNIFORM:if(d>e){c.width=l*e;c.height=g*e}else{c.width=l*d;c.height=g*d}break;case b.utils.stretching.FILL:if(d>e){c.width=l*d;c.height=g*d}else{c.width=l*e;c.height=g*e}break;case b.utils.stretching.EXACTFIT:b.utils.transform(n,["scale(",d,",",e,")"," translate(0px,0px)"].join(""));c.width=l;c.height=g;break;default:break}c.top=(f-c.height)/2;c.left=(m-c.width)/2;b.utils.css(n,c)};b.utils.stretching={NONE:"NONE",FILL:"FILL",UNIFORM:"UNIFORM",EXACTFIT:"EXACTFIT"};b.utils.deepReplaceKeyName=function(h,e,c){switch(b.utils.typeOf(h)){case"array":for(var g=0;g<h.length;g++){h[g]=b.utils.deepReplaceKeyName(h[g],e,c)}break;case"object":for(var f in h){var d=f.replace(new RegExp(e,"g"),c);h[d]=b.utils.deepReplaceKeyName(h[f],e,c);if(f!=d){delete h[f]}}break}return h};b.utils.isInArray=function(e,d){if(!(e)||!(e instanceof Array)){return false}for(var c=0;c<e.length;c++){if(d===e[c]){return true}}return false};b.utils.exists=function(c){switch(typeof(c)){case"string":return(c.length>0);break;case"object":return(c!==null);case"undefined":return false}return true};b.utils.empty=function(c){if(typeof c.hasChildNodes=="function"){while(c.hasChildNodes()){c.removeChild(c.firstChild)}}};b.utils.parseDimension=function(c){if(typeof c=="string"){if(c===""){return 0}else{if(c.lastIndexOf("%")>-1){return c}else{return parseInt(c.replace("px",""),10)}}}return c};b.utils.getDimensions=function(c){if(c&&c.style){return{x:b.utils.parseDimension(c.style.left),y:b.utils.parseDimension(c.style.top),width:b.utils.parseDimension(c.style.width),height:b.utils.parseDimension(c.style.height)}}else{return{}}};b.utils.timeFormat=function(c){str="00:00";if(c>0){str=Math.floor(c/60)<10?"0"+Math.floor(c/60)+":":Math.floor(c/60)+":";str+=Math.floor(c%60)<10?"0"+Math.floor(c%60):Math.floor(c%60)}return str}})(jwplayer);(function(a){a.events=function(){};a.events.COMPLETE="COMPLETE";a.events.ERROR="ERROR"})(jwplayer);(function(jwplayer){jwplayer.events.eventdispatcher=function(debug){var _debug=debug;var _listeners;var _globallisteners;this.resetEventListeners=function(){_listeners={};_globallisteners=[]};this.resetEventListeners();this.addEventListener=function(type,listener,count){try{if(!jwplayer.utils.exists(_listeners[type])){_listeners[type]=[]}if(typeof(listener)=="string"){eval("listener = "+listener)}_listeners[type].push({listener:listener,count:count})}catch(err){jwplayer.utils.log("error",err)}return false};this.removeEventListener=function(type,listener){if(!_listeners[type]){return}try{for(var listenerIndex=0;listenerIndex<_listeners[type].length;listenerIndex++){if(_listeners[type][listenerIndex].listener.toString()==listener.toString()){_listeners[type].splice(listenerIndex,1);break}}}catch(err){jwplayer.utils.log("error",err)}return false};this.addGlobalListener=function(listener,count){try{if(typeof(listener)=="string"){eval("listener = "+listener)}_globallisteners.push({listener:listener,count:count})}catch(err){jwplayer.utils.log("error",err)}return false};this.removeGlobalListener=function(listener){if(!_globallisteners[type]){return}try{for(var globalListenerIndex=0;globalListenerIndex<_globallisteners.length;globalListenerIndex++){if(_globallisteners[globalListenerIndex].listener.toString()==listener.toString()){_globallisteners.splice(globalListenerIndex,1);break}}}catch(err){jwplayer.utils.log("error",err)}return false};this.sendEvent=function(type,data){if(!jwplayer.utils.exists(data)){data={}}if(_debug){jwplayer.utils.log(type,data)}if(typeof _listeners[type]!="undefined"){for(var listenerIndex=0;listenerIndex<_listeners[type].length;listenerIndex++){try{_listeners[type][listenerIndex].listener(data)}catch(err){jwplayer.utils.log("There was an error while handling a listener: "+err.toString(),_listeners[type][listenerIndex].listener)}if(_listeners[type][listenerIndex]){if(_listeners[type][listenerIndex].count===1){delete _listeners[type][listenerIndex]}else{if(_listeners[type][listenerIndex].count>0){_listeners[type][listenerIndex].count=_listeners[type][listenerIndex].count-1}}}}}for(var globalListenerIndex=0;globalListenerIndex<_globallisteners.length;globalListenerIndex++){try{_globallisteners[globalListenerIndex].listener(data)}catch(err){jwplayer.utils.log("There was an error while handling a listener: "+err.toString(),_globallisteners[globalListenerIndex].listener)}if(_globallisteners[globalListenerIndex]){if(_globallisteners[globalListenerIndex].count===1){delete _globallisteners[globalListenerIndex]}else{if(_globallisteners[globalListenerIndex].count>0){_globallisteners[globalListenerIndex].count=_globallisteners[globalListenerIndex].count-1}}}}}}})(jwplayer);(function(a){var b={};a.utils.animations=function(){};a.utils.animations.transform=function(c,d){c.style.webkitTransform=d;c.style.MozTransform=d;c.style.OTransform=d;c.style.msTransform=d};a.utils.animations.transformOrigin=function(c,d){c.style.webkitTransformOrigin=d;c.style.MozTransformOrigin=d;c.style.OTransformOrigin=d;c.style.msTransformOrigin=d};a.utils.animations.rotate=function(c,d){a.utils.animations.transform(c,["rotate(",d,"deg)"].join(""))};a.utils.cancelAnimation=function(c){delete b[c.id]};a.utils.fadeTo=function(m,f,e,j,h,d){if(b[m.id]!=d&&a.utils.exists(d)){return}if(m.style.opacity==f){return}var c=new Date().getTime();if(d>c){setTimeout(function(){a.utils.fadeTo(m,f,e,j,0,d)},d-c)}if(m.style.display=="none"){m.style.display="block"}if(!a.utils.exists(j)){j=m.style.opacity===""?1:m.style.opacity}if(m.style.opacity==f&&m.style.opacity!==""&&a.utils.exists(d)){if(f===0){m.style.display="none"}return}if(!a.utils.exists(d)){d=c;b[m.id]=d}if(!a.utils.exists(h)){h=0}var k=(e>0)?((c-d)/(e*1000)):0;k=k>1?1:k;var l=f-j;var g=j+(k*l);if(g>1){g=1}else{if(g<0){g=0}}m.style.opacity=g;if(h>0){b[m.id]=d+h*1000;a.utils.fadeTo(m,f,e,j,0,b[m.id]);return}setTimeout(function(){a.utils.fadeTo(m,f,e,j,0,d)},10)}})(jwplayer);(function(a){a.utils.arrays=function(){};a.utils.arrays.indexOf=function(c,d){for(var b=0;b<c.length;b++){if(c[b]==d){return b}}return -1};a.utils.arrays.remove=function(c,d){var b=a.utils.arrays.indexOf(c,d);if(b>-1){c.splice(b,1)}}})(jwplayer);(function(a){a.utils.extensionmap={"3gp":{html5:"video/3gpp",flash:"video"},"3gpp":{html5:"video/3gpp"},"3g2":{html5:"video/3gpp2",flash:"video"},"3gpp2":{html5:"video/3gpp2"},flv:{flash:"video"},f4a:{html5:"audio/mp4"},f4b:{html5:"audio/mp4",flash:"video"},f4v:{html5:"video/mp4",flash:"video"},mov:{html5:"video/quicktime",flash:"video"},m4a:{html5:"audio/mp4",flash:"video"},m4b:{html5:"audio/mp4"},m4p:{html5:"audio/mp4"},m4v:{html5:"video/mp4",flash:"video"},mp4:{html5:"video/mp4",flash:"video"},rbs:{flash:"sound"},aac:{html5:"audio/aac",flash:"video"},mp3:{html5:"audio/mp3",flash:"sound"},ogg:{html5:"audio/ogg"},oga:{html5:"audio/ogg"},ogv:{html5:"video/ogg"},webm:{html5:"video/webm"},m3u8:{html5:"audio/x-mpegurl"},gif:{flash:"image"},jpeg:{flash:"image"},jpg:{flash:"image"},swf:{flash:"image"},png:{flash:"image"},wav:{html5:"audio/x-wav"}}})(jwplayer);(function(e){e.utils.mediaparser=function(){};var g={element:{width:"width",height:"height",id:"id","class":"className",name:"name"},media:{src:"file",preload:"preload",autoplay:"autostart",loop:"repeat",controls:"controls"},source:{src:"file",type:"type",media:"media","data-jw-width":"width","data-jw-bitrate":"bitrate"},video:{poster:"image"}};var f={};e.utils.mediaparser.parseMedia=function(j){return d(j)};function c(k,j){if(!e.utils.exists(j)){j=g[k]}else{e.utils.extend(j,g[k])}return j}function d(n,j){if(f[n.tagName.toLowerCase()]&&!e.utils.exists(j)){return f[n.tagName.toLowerCase()](n)}else{j=c("element",j);var o={};for(var k in j){if(k!="length"){var m=n.getAttribute(k);if(e.utils.exists(m)){o[j[k]]=m}}}var l=n.style["#background-color"];if(l&&!(l=="transparent"||l=="rgba(0, 0, 0, 0)")){o.screencolor=l}return o}}function h(n,k){k=c("media",k);var l=[];var j=e.utils.selectors("source",n);for(var m in j){if(!isNaN(m)){l.push(a(j[m]))}}var o=d(n,k);if(e.utils.exists(o.file)){l[0]={file:o.file}}o.levels=l;return o}function a(l,k){k=c("source",k);var j=d(l,k);j.width=j.width?j.width:0;j.bitrate=j.bitrate?j.bitrate:0;return j}function b(l,k){k=c("video",k);var j=h(l,k);return j}f.media=h;f.audio=h;f.source=a;f.video=b})(jwplayer);(function(a){a.utils.loaderstatus={NEW:"NEW",LOADING:"LOADING",ERROR:"ERROR",COMPLETE:"COMPLETE"};a.utils.scriptloader=function(c){var d=a.utils.loaderstatus.NEW;var b=new a.events.eventdispatcher();a.utils.extend(this,b);this.load=function(){if(d==a.utils.loaderstatus.NEW){d=a.utils.loaderstatus.LOADING;var e=document.createElement("script");e.onload=function(f){d=a.utils.loaderstatus.COMPLETE;b.sendEvent(a.events.COMPLETE)};e.onerror=function(f){d=a.utils.loaderstatus.ERROR;b.sendEvent(a.events.ERROR)};e.onreadystatechange=function(){if(e.readyState=="loaded"||e.readyState=="complete"){d=a.utils.loaderstatus.COMPLETE;b.sendEvent(a.events.COMPLETE)}};document.getElementsByTagName("head")[0].appendChild(e);e.src=c}};this.getStatus=function(){return d}}})(jwplayer);(function(a){a.utils.selectors=function(b,e){if(!a.utils.exists(e)){e=document}b=a.utils.strings.trim(b);var c=b.charAt(0);if(c=="#"){return e.getElementById(b.substr(1))}else{if(c=="."){if(e.getElementsByClassName){return e.getElementsByClassName(b.substr(1))}else{return a.utils.selectors.getElementsByTagAndClass("*",b.substr(1))}}else{if(b.indexOf(".")>0){var d=b.split(".");return a.utils.selectors.getElementsByTagAndClass(d[0],d[1])}else{return e.getElementsByTagName(b)}}}return null};a.utils.selectors.getElementsByTagAndClass=function(e,h,g){var j=[];if(!a.utils.exists(g)){g=document}var f=g.getElementsByTagName(e);for(var d=0;d<f.length;d++){if(a.utils.exists(f[d].className)){var c=f[d].className.split(" ");for(var b=0;b<c.length;b++){if(c[b]==h){j.push(f[d])}}}}return j}})(jwplayer);(function(a){a.utils.strings=function(){};a.utils.strings.trim=function(b){return b.replace(/^\s*/,"").replace(/\s*$/,"")};a.utils.strings.pad=function(c,d,b){if(!b){b="0"}while(c.length<d){c=b+c}return c};a.utils.strings.serialize=function(b){if(b==null){return null}else{if(b=="true"){return true}else{if(b=="false"){return false}else{if(isNaN(Number(b))||b.length>5||b.length==0){return b}else{return Number(b)}}}}};a.utils.strings.seconds=function(d){d=d.replace(",",".");var b=d.split(":");var c=0;if(d.substr(-1)=="s"){c=Number(d.substr(0,d.length-1))}else{if(d.substr(-1)=="m"){c=Number(d.substr(0,d.length-1))*60}else{if(d.substr(-1)=="h"){c=Number(d.substr(0,d.length-1))*3600}else{if(b.length>1){c=Number(b[b.length-1]);c+=Number(b[b.length-2])*60;if(b.length==3){c+=Number(b[b.length-3])*3600}}else{c=Number(d)}}}}return c};a.utils.strings.xmlAttribute=function(b,c){for(var d=0;d<b.attributes.length;d++){if(b.attributes[d].name&&b.attributes[d].name.toLowerCase()==c.toLowerCase()){return b.attributes[d].value.toString()}}return""};a.utils.strings.jsonToString=function(f){var h=h||{};if(h&&h.stringify){return h.stringify(f)}var c=typeof(f);if(c!="object"||f===null){if(c=="string"){f='"'+f+'"'}else{return String(f)}}else{var g=[],b=(f&&f.constructor==Array);for(var d in f){var e=f[d];switch(typeof(e)){case"string":e='"'+e+'"';break;case"object":if(a.utils.exists(e)){e=a.utils.strings.jsonToString(e)}break}if(b){if(typeof(e)!="function"){g.push(String(e))}}else{if(typeof(e)!="function"){g.push('"'+d+'":'+String(e))}}}if(b){return"["+String(g)+"]"}else{return"{"+String(g)+"}"}}}})(jwplayer);(function(c){var d=new RegExp(/^(#|0x)[0-9a-fA-F]{3,6}/);c.utils.typechecker=function(g,f){f=!c.utils.exists(f)?b(g):f;return e(g,f)};function b(f){var g=["true","false","t","f"];if(g.toString().indexOf(f.toLowerCase().replace(" ",""))>=0){return"boolean"}else{if(d.test(f)){return"color"}else{if(!isNaN(parseInt(f,10))&&parseInt(f,10).toString().length==f.length){return"integer"}else{if(!isNaN(parseFloat(f))&&parseFloat(f).toString().length==f.length){return"float"}}}}return"string"}function e(g,f){if(!c.utils.exists(f)){return g}switch(f){case"color":if(g.length>0){return a(g)}return null;case"integer":return parseInt(g,10);case"float":return parseFloat(g);case"boolean":if(g.toLowerCase()=="true"){return true}else{if(g=="1"){return true}}return false}return g}function a(f){switch(f.toLowerCase()){case"blue":return parseInt("0000FF",16);case"green":return parseInt("00FF00",16);case"red":return parseInt("FF0000",16);case"cyan":return parseInt("00FFFF",16);case"magenta":return parseInt("FF00FF",16);case"yellow":return parseInt("FFFF00",16);case"black":return parseInt("000000",16);case"white":return parseInt("FFFFFF",16);default:f=f.replace(/(#|0x)?([0-9A-F]{3,6})$/gi,"$2");if(f.length==3){f=f.charAt(0)+f.charAt(0)+f.charAt(1)+f.charAt(1)+f.charAt(2)+f.charAt(2)}return parseInt(f,16)}return parseInt("000000",16)}})(jwplayer);(function(a){a.utils.parsers=function(){};a.utils.parsers.localName=function(b){if(!b){return""}else{if(b.localName){return b.localName}else{if(b.baseName){return b.baseName}else{return""}}}};a.utils.parsers.textContent=function(b){if(!b){return""}else{if(b.textContent){return b.textContent}else{if(b.text){return b.text}else{return""}}}}})(jwplayer);(function(a){a.utils.parsers.jwparser=function(){};a.utils.parsers.jwparser.PREFIX="jwplayer";a.utils.parsers.jwparser.parseEntry=function(c,d){for(var b=0;b<c.childNodes.length;b++){if(c.childNodes[b].prefix==a.utils.parsers.jwparser.PREFIX){d[a.utils.parsers.localName(c.childNodes[b])]=a.utils.strings.serialize(a.utils.parsers.textContent(c.childNodes[b]))}if(!d.file&&String(d.link).toLowerCase().indexOf("youtube")>-1){d.file=d.link}}return d};a.utils.parsers.jwparser.getProvider=function(c){if(c.type){return c.type}else{if(c.file.indexOf("youtube.com/w")>-1||c.file.indexOf("youtube.com/v")>-1||c.file.indexOf("youtu.be/")>-1){return"youtube"}else{if(c.streamer&&c.streamer.indexOf("rtmp")==0){return"rtmp"}else{if(c.streamer&&c.streamer.indexOf("http")==0){return"http"}else{var b=a.utils.strings.extension(c.file);if(extensions.hasOwnProperty(b)){return extensions[b]}}}}}return""}})(jwplayer);(function(a){a.utils.parsers.mediaparser=function(){};a.utils.parsers.mediaparser.PREFIX="media";a.utils.parsers.mediaparser.parseGroup=function(d,f){var e=false;for(var c=0;c<d.childNodes.length;c++){if(d.childNodes[c].prefix==a.utils.parsers.mediaparser.PREFIX){if(!a.utils.parsers.localName(d.childNodes[c])){continue}switch(a.utils.parsers.localName(d.childNodes[c]).toLowerCase()){case"content":if(!e){f.file=a.utils.strings.xmlAttribute(d.childNodes[c],"url")}if(a.utils.strings.xmlAttribute(d.childNodes[c],"duration")){f.duration=a.utils.strings.seconds(a.utils.strings.xmlAttribute(d.childNodes[c],"duration"))}if(a.utils.strings.xmlAttribute(d.childNodes[c],"start")){f.start=a.utils.strings.seconds(a.utils.strings.xmlAttribute(d.childNodes[c],"start"))}if(d.childNodes[c].childNodes&&d.childNodes[c].childNodes.length>0){f=a.utils.parsers.mediaparser.parseGroup(d.childNodes[c],f)}if(a.utils.strings.xmlAttribute(d.childNodes[c],"width")||a.utils.strings.xmlAttribute(d.childNodes[c],"bitrate")||a.utils.strings.xmlAttribute(d.childNodes[c],"url")){if(!f.levels){f.levels=[]}f.levels.push({width:a.utils.strings.xmlAttribute(d.childNodes[c],"width"),bitrate:a.utils.strings.xmlAttribute(d.childNodes[c],"bitrate"),file:a.utils.strings.xmlAttribute(d.childNodes[c],"url")})}break;case"title":f.title=a.utils.parsers.textContent(d.childNodes[c]);break;case"description":f.description=a.utils.parsers.textContent(d.childNodes[c]);break;case"keywords":f.tags=a.utils.parsers.textContent(d.childNodes[c]);break;case"thumbnail":f.image=a.utils.strings.xmlAttribute(d.childNodes[c],"url");break;case"credit":f.author=a.utils.parsers.textContent(d.childNodes[c]);break;case"player":var b=d.childNodes[c].url;if(b.indexOf("youtube.com")>=0||b.indexOf("youtu.be")>=0){e=true;f.file=a.utils.strings.xmlAttribute(d.childNodes[c],"url")}break;case"group":a.utils.parsers.mediaparser.parseGroup(d.childNodes[c],f);break}}}return f}})(jwplayer);(function(b){b.utils.parsers.rssparser=function(){};b.utils.parsers.rssparser.parse=function(f){var c=[];for(var e=0;e<f.childNodes.length;e++){if(b.utils.parsers.localName(f.childNodes[e]).toLowerCase()=="channel"){for(var d=0;d<f.childNodes[e].childNodes.length;d++){if(b.utils.parsers.localName(f.childNodes[e].childNodes[d]).toLowerCase()=="item"){c.push(a(f.childNodes[e].childNodes[d]))}}}}return c};function a(d){var e={};for(var c=0;c<d.childNodes.length;c++){if(!b.utils.parsers.localName(d.childNodes[c])){continue}switch(b.utils.parsers.localName(d.childNodes[c]).toLowerCase()){case"enclosure":e.file=b.utils.strings.xmlAttribute(d.childNodes[c],"url");break;case"title":e.title=b.utils.parsers.textContent(d.childNodes[c]);break;case"pubdate":e.date=b.utils.parsers.textContent(d.childNodes[c]);break;case"description":e.description=b.utils.parsers.textContent(d.childNodes[c]);break;case"link":e.link=b.utils.parsers.textContent(d.childNodes[c]);break;case"category":if(e.tags){e.tags+=b.utils.parsers.textContent(d.childNodes[c])}else{e.tags=b.utils.parsers.textContent(d.childNodes[c])}break}}e=b.utils.parsers.mediaparser.parseGroup(d,e);e=b.utils.parsers.jwparser.parseEntry(d,e);return new b.html5.playlistitem(e)}})(jwplayer);(function(a){var c={};var b={};a.plugins=function(){};a.plugins.loadPlugins=function(e,d){b[e]=new a.plugins.pluginloader(new a.plugins.model(c),d);return b[e]};a.plugins.registerPlugin=function(h,f,e){var d=a.utils.getPluginName(h);if(c[d]){c[d].registerPlugin(h,f,e)}else{a.utils.log("A plugin ("+h+") was registered with the player that was not loaded. Please check your configuration.");for(var g in b){b[g].pluginFailed()}}}})(jwplayer);(function(a){a.plugins.model=function(b){this.addPlugin=function(c){var d=a.utils.getPluginName(c);if(!b[d]){b[d]=new a.plugins.plugin(c)}return b[d]}}})(jwplayer);(function(a){a.plugins.pluginmodes={FLASH:"FLASH",JAVASCRIPT:"JAVASCRIPT",HYBRID:"HYBRID"};a.plugins.plugin=function(b){var d="http://plugins.longtailvideo.com";var j=a.utils.loaderstatus.NEW;var k;var h;var l;var c=new a.events.eventdispatcher();a.utils.extend(this,c);function e(){switch(a.utils.getPluginPathType(b)){case a.utils.pluginPathType.ABSOLUTE:return b;case a.utils.pluginPathType.RELATIVE:return a.utils.getAbsolutePath(b,window.location.href);case a.utils.pluginPathType.CDN:var n=a.utils.getPluginName(b);var m=a.utils.getPluginVersion(b);return d+"/"+a.version.split(".")[0]+"/"+n+"/"+n+(m!==""?("-"+m):"")+".js"}}function g(m){l=setTimeout(function(){j=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE)},1000)}function f(m){j=a.utils.loaderstatus.ERROR;c.sendEvent(a.events.ERROR)}this.load=function(){if(j==a.utils.loaderstatus.NEW){if(b.lastIndexOf(".swf")>0){k=b;j=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE);return}j=a.utils.loaderstatus.LOADING;var m=new a.utils.scriptloader(e());m.addEventListener(a.events.COMPLETE,g);m.addEventListener(a.events.ERROR,f);m.load()}};this.registerPlugin=function(o,n,m){if(l){clearTimeout(l);l=undefined}if(n&&m){k=m;h=n}else{if(typeof n=="string"){k=n}else{if(typeof n=="function"){h=n}else{if(!n&&!m){k=o}}}}j=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE)};this.getStatus=function(){return j};this.getPluginName=function(){return a.utils.getPluginName(b)};this.getFlashPath=function(){if(k){switch(a.utils.getPluginPathType(k)){case a.utils.pluginPathType.ABSOLUTE:return k;case a.utils.pluginPathType.RELATIVE:if(b.lastIndexOf(".swf")>0){return a.utils.getAbsolutePath(k,window.location.href)}return a.utils.getAbsolutePath(k,e());case a.utils.pluginPathType.CDN:if(k.indexOf("-")>-1){return k+"h"}return k+"-h"}}return null};this.getJS=function(){return h};this.getPluginmode=function(){if(typeof k!="undefined"&&typeof h!="undefined"){return a.plugins.pluginmodes.HYBRID}else{if(typeof k!="undefined"){return a.plugins.pluginmodes.FLASH}else{if(typeof h!="undefined"){return a.plugins.pluginmodes.JAVASCRIPT}}}};this.getNewInstance=function(n,m,o){return new h(n,m,o)};this.getURL=function(){return b}}})(jwplayer);(function(a){a.plugins.pluginloader=function(h,e){var g={};var k=a.utils.loaderstatus.NEW;var d=false;var b=false;var c=new a.events.eventdispatcher();a.utils.extend(this,c);function f(){if(!b){b=true;k=a.utils.loaderstatus.COMPLETE;c.sendEvent(a.events.COMPLETE)}}function j(){if(!b){var m=0;for(plugin in g){var l=g[plugin].getStatus();if(l==a.utils.loaderstatus.LOADING||l==a.utils.loaderstatus.NEW){m++}}if(m==0){f()}}}this.setupPlugins=function(n,l,s){var m={length:0,plugins:{}};var p={length:0,plugins:{}};for(var o in g){var q=g[o].getPluginName();if(g[o].getFlashPath()){m.plugins[g[o].getFlashPath()]=l.plugins[o];m.plugins[g[o].getFlashPath()].pluginmode=g[o].getPluginmode();m.length++}if(g[o].getJS()){var r=document.createElement("div");r.id=n.id+"_"+q;r.style.position="absolute";r.style.zIndex=p.length+10;p.plugins[q]=g[o].getNewInstance(n,l.plugins[o],r);p.length++;if(typeof p.plugins[q].resize!="undefined"){n.onReady(s(p.plugins[q],r,true));n.onResize(s(p.plugins[q],r))}}}n.plugins=p.plugins;return m};this.load=function(){k=a.utils.loaderstatus.LOADING;d=true;for(var l in e){if(a.utils.exists(l)){g[l]=h.addPlugin(l);g[l].addEventListener(a.events.COMPLETE,j);g[l].addEventListener(a.events.ERROR,j)}}for(l in g){g[l].load()}d=false;j()};this.pluginFailed=function(){f()};this.getStatus=function(){return k}}})(jwplayer);(function(b){var a=[];b.api=function(d){this.container=d;this.id=d.id;var n={};var s={};var q={};var c=[];var h=undefined;var l=false;var j=[];var p=b.utils.getOuterHTML(d);var r={};var k={};this.getBuffer=function(){return this.callInternal("jwGetBuffer")};this.getContainer=function(){return this.container};function e(u,t){return function(z,v,w,x){if(u.renderingMode=="flash"||u.renderingMode=="html5"){var y;if(v){k[z]=v;y="jwplayer('"+u.id+"').callback('"+z+"')"}else{if(!v&&k[z]){delete k[z]}}h.jwDockSetButton(z,y,w,x)}return t}}this.getPlugin=function(t){var v=this;var u={};if(t=="dock"){return b.utils.extend(u,{setButton:e(v,u),show:function(){v.callInternal("jwDockShow");return u},hide:function(){v.callInternal("jwDockHide");return u},onShow:function(w){v.componentListener("dock",b.api.events.JWPLAYER_COMPONENT_SHOW,w);return u},onHide:function(w){v.componentListener("dock",b.api.events.JWPLAYER_COMPONENT_HIDE,w);return u}})}else{if(t=="controlbar"){return b.utils.extend(u,{show:function(){v.callInternal("jwControlbarShow");return u},hide:function(){v.callInternal("jwControlbarHide");return u},onShow:function(w){v.componentListener("controlbar",b.api.events.JWPLAYER_COMPONENT_SHOW,w);return u},onHide:function(w){v.componentListener("controlbar",b.api.events.JWPLAYER_COMPONENT_HIDE,w);return u}})}else{if(t=="display"){return b.utils.extend(u,{show:function(){v.callInternal("jwDisplayShow");return u},hide:function(){v.callInternal("jwDisplayHide");return u},onShow:function(w){v.componentListener("display",b.api.events.JWPLAYER_COMPONENT_SHOW,w);return u},onHide:function(w){v.componentListener("display",b.api.events.JWPLAYER_COMPONENT_HIDE,w);return u}})}else{return this.plugins[t]}}}};this.callback=function(t){if(k[t]){return k[t]()}};this.getDuration=function(){return this.callInternal("jwGetDuration")};this.getFullscreen=function(){return this.callInternal("jwGetFullscreen")};this.getHeight=function(){return this.callInternal("jwGetHeight")};this.getLockState=function(){return this.callInternal("jwGetLockState")};this.getMeta=function(){return this.getItemMeta()};this.getMute=function(){return this.callInternal("jwGetMute")};this.getPlaylist=function(){var u=this.callInternal("jwGetPlaylist");if(this.renderingMode=="flash"){b.utils.deepReplaceKeyName(u,"__dot__",".")}for(var t=0;t<u.length;t++){if(!b.utils.exists(u[t].index)){u[t].index=t}}return u};this.getPlaylistItem=function(t){if(!b.utils.exists(t)){t=this.getCurrentItem()}return this.getPlaylist()[t]};this.getPosition=function(){return this.callInternal("jwGetPosition")};this.getRenderingMode=function(){return this.renderingMode};this.getState=function(){return this.callInternal("jwGetState")};this.getVolume=function(){return this.callInternal("jwGetVolume")};this.getWidth=function(){return this.callInternal("jwGetWidth")};this.setFullscreen=function(t){if(!b.utils.exists(t)){this.callInternal("jwSetFullscreen",!this.callInternal("jwGetFullscreen"))}else{this.callInternal("jwSetFullscreen",t)}return this};this.setMute=function(t){if(!b.utils.exists(t)){this.callInternal("jwSetMute",!this.callInternal("jwGetMute"))}else{this.callInternal("jwSetMute",t)}return this};this.lock=function(){return this};this.unlock=function(){return this};this.load=function(t){this.callInternal("jwLoad",t);return this};this.playlistItem=function(t){this.callInternal("jwPlaylistItem",t);return this};this.playlistPrev=function(){this.callInternal("jwPlaylistPrev");return this};this.playlistNext=function(){this.callInternal("jwPlaylistNext");return this};this.resize=function(u,t){if(this.renderingMode=="html5"){h.jwResize(u,t)}else{this.container.width=u;this.container.height=t}return this};this.play=function(t){if(typeof t=="undefined"){t=this.getState();if(t==b.api.events.state.PLAYING||t==b.api.events.state.BUFFERING){this.callInternal("jwPause")}else{this.callInternal("jwPlay")}}else{this.callInternal("jwPlay",t)}return this};this.pause=function(t){if(typeof t=="undefined"){t=this.getState();if(t==b.api.events.state.PLAYING||t==b.api.events.state.BUFFERING){this.callInternal("jwPause")}else{this.callInternal("jwPlay")}}else{this.callInternal("jwPause",t)}return this};this.stop=function(){this.callInternal("jwStop");return this};this.seek=function(t){this.callInternal("jwSeek",t);return this};this.setVolume=function(t){this.callInternal("jwSetVolume",t);return this};this.onBufferChange=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_BUFFER,t)};this.onBufferFull=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_BUFFER_FULL,t)};this.onError=function(t){return this.eventListener(b.api.events.JWPLAYER_ERROR,t)};this.onFullscreen=function(t){return this.eventListener(b.api.events.JWPLAYER_FULLSCREEN,t)};this.onMeta=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_META,t)};this.onMute=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_MUTE,t)};this.onPlaylist=function(t){return this.eventListener(b.api.events.JWPLAYER_PLAYLIST_LOADED,t)};this.onPlaylistItem=function(t){return this.eventListener(b.api.events.JWPLAYER_PLAYLIST_ITEM,t)};this.onReady=function(t){return this.eventListener(b.api.events.API_READY,t)};this.onResize=function(t){return this.eventListener(b.api.events.JWPLAYER_RESIZE,t)};this.onComplete=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_COMPLETE,t)};this.onSeek=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_SEEK,t)};this.onTime=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_TIME,t)};this.onVolume=function(t){return this.eventListener(b.api.events.JWPLAYER_MEDIA_VOLUME,t)};this.onBuffer=function(t){return this.stateListener(b.api.events.state.BUFFERING,t)};this.onPause=function(t){return this.stateListener(b.api.events.state.PAUSED,t)};this.onPlay=function(t){return this.stateListener(b.api.events.state.PLAYING,t)};this.onIdle=function(t){return this.stateListener(b.api.events.state.IDLE,t)};this.remove=function(){n={};j=[];if(b.utils.getOuterHTML(this.container)!=p){b.api.destroyPlayer(this.id,p)}};this.setup=function(u){if(b.embed){var t=this.id;this.remove();var v=b(t);v.config=u;return new b.embed(v)}return this};this.registerPlugin=function(v,u,t){b.plugins.registerPlugin(v,u,t)};this.setPlayer=function(t,u){h=t;this.renderingMode=u};this.stateListener=function(t,u){if(!s[t]){s[t]=[];this.eventListener(b.api.events.JWPLAYER_PLAYER_STATE,g(t))}s[t].push(u);return this};function g(t){return function(v){var u=v.newstate,x=v.oldstate;if(u==t){var w=s[u];if(w){for(var y=0;y<w.length;y++){if(typeof w[y]=="function"){w[y].call(this,{oldstate:x,newstate:u})}}}}}}this.componentListener=function(t,u,v){if(!q[t]){q[t]={}}if(!q[t][u]){q[t][u]=[];this.eventListener(u,m(t,u))}q[t][u].push(v);return this};function m(t,u){return function(w){if(t==w.component){var v=q[t][u];if(v){for(var x=0;x<v.length;x++){if(typeof v[x]=="function"){v[x].call(this,w)}}}}}}this.addInternalListener=function(t,u){t.jwAddEventListener(u,'function(dat) { jwplayer("'+this.id+'").dispatchEvent("'+u+'", dat); }')};this.eventListener=function(t,u){if(!n[t]){n[t]=[];if(h&&l){this.addInternalListener(h,t)}}n[t].push(u);return this};this.dispatchEvent=function(v){if(n[v]){var u=f(v,arguments[1]);for(var t=0;t<n[v].length;t++){if(typeof n[v][t]=="function"){n[v][t].call(this,u)}}}};function f(v,t){var x=b.utils.extend({},t);if(v==b.api.events.JWPLAYER_FULLSCREEN&&!x.fullscreen){x.fullscreen=x.message=="true"?true:false;delete x.message}else{if(typeof x.data=="object"){x=b.utils.extend(x,x.data);delete x.data}}var u=["position","duration","offset"];for(var w in u){if(x[u[w]]){x[u[w]]=Math.round(x[u[w]]*1000)/1000}}return x}this.callInternal=function(u,t){if(l){if(typeof h!="undefined"&&typeof h[u]=="function"){if(b.utils.exists(t)){return(h[u])(t)}else{return(h[u])()}}return null}else{j.push({method:u,parameters:t})}};this.playerReady=function(v){l=true;if(!h){this.setPlayer(document.getElementById(v.id))}this.container=document.getElementById(this.id);for(var t in n){this.addInternalListener(h,t)}this.eventListener(b.api.events.JWPLAYER_PLAYLIST_ITEM,function(w){r={}});this.eventListener(b.api.events.JWPLAYER_MEDIA_META,function(w){b.utils.extend(r,w.metadata)});this.dispatchEvent(b.api.events.API_READY);while(j.length>0){var u=j.shift();this.callInternal(u.method,u.parameters)}};this.getItemMeta=function(){return r};this.getCurrentItem=function(){return this.callInternal("jwGetPlaylistIndex")};function o(v,x,w){var t=[];if(!x){x=0}if(!w){w=v.length-1}for(var u=x;u<=w;u++){t.push(v[u])}return t}return this};b.api.selectPlayer=function(d){var c;if(!b.utils.exists(d)){d=0}if(d.nodeType){c=d}else{if(typeof d=="string"){c=document.getElementById(d)}}if(c){var e=b.api.playerById(c.id);if(e){return e}else{return b.api.addPlayer(new b.api(c))}}else{if(typeof d=="number"){return b.getPlayers()[d]}}return null};b.api.events={API_READY:"jwplayerAPIReady",JWPLAYER_READY:"jwplayerReady",JWPLAYER_FULLSCREEN:"jwplayerFullscreen",JWPLAYER_RESIZE:"jwplayerResize",JWPLAYER_ERROR:"jwplayerError",JWPLAYER_COMPONENT_SHOW:"jwplayerComponentShow",JWPLAYER_COMPONENT_HIDE:"jwplayerComponentHide",JWPLAYER_MEDIA_BUFFER:"jwplayerMediaBuffer",JWPLAYER_MEDIA_BUFFER_FULL:"jwplayerMediaBufferFull",JWPLAYER_MEDIA_ERROR:"jwplayerMediaError",JWPLAYER_MEDIA_LOADED:"jwplayerMediaLoaded",JWPLAYER_MEDIA_COMPLETE:"jwplayerMediaComplete",JWPLAYER_MEDIA_SEEK:"jwplayerMediaSeek",JWPLAYER_MEDIA_TIME:"jwplayerMediaTime",JWPLAYER_MEDIA_VOLUME:"jwplayerMediaVolume",JWPLAYER_MEDIA_META:"jwplayerMediaMeta",JWPLAYER_MEDIA_MUTE:"jwplayerMediaMute",JWPLAYER_PLAYER_STATE:"jwplayerPlayerState",JWPLAYER_PLAYLIST_LOADED:"jwplayerPlaylistLoaded",JWPLAYER_PLAYLIST_ITEM:"jwplayerPlaylistItem"};b.api.events.state={BUFFERING:"BUFFERING",IDLE:"IDLE",PAUSED:"PAUSED",PLAYING:"PLAYING"};b.api.playerById=function(d){for(var c=0;c<a.length;c++){if(a[c].id==d){return a[c]}}return null};b.api.addPlayer=function(c){for(var d=0;d<a.length;d++){if(a[d]==c){return c}}a.push(c);return c};b.api.destroyPlayer=function(g,d){var f=-1;for(var j=0;j<a.length;j++){if(a[j].id==g){f=j;continue}}if(f>=0){var c=document.getElementById(a[f].id);if(document.getElementById(a[f].id+"_wrapper")){c=document.getElementById(a[f].id+"_wrapper")}if(c){if(d){b.utils.setOuterHTML(c,d)}else{var h=document.createElement("div");var e=c.id;if(c.id.indexOf("_wrapper")==c.id.length-8){newID=c.id.substring(0,c.id.length-8)}h.setAttribute("id",e);c.parentNode.replaceChild(h,c)}}a.splice(f,1)}return null};b.getPlayers=function(){return a.slice(0)}})(jwplayer);var _userPlayerReady=(typeof playerReady=="function")?playerReady:undefined;playerReady=function(b){var a=jwplayer.api.playerById(b.id);if(a){a.playerReady(b)}else{jwplayer.api.selectPlayer(b.id).playerReady(b)}if(_userPlayerReady){_userPlayerReady.call(this,b)}};(function(a){a.embed=function(g){var j={width:400,height:300,components:{controlbar:{position:"over"}}};var f=a.utils.mediaparser.parseMedia(g.container);var e=new a.embed.config(a.utils.extend(j,f,g.config),this);var h=a.plugins.loadPlugins(g.id,e.plugins);function c(m,l){for(var k in l){if(typeof m[k]=="function"){(m[k]).call(m,l[k])}}}function d(){if(h.getStatus()==a.utils.loaderstatus.COMPLETE){for(var m=0;m<e.modes.length;m++){if(e.modes[m].type&&a.embed[e.modes[m].type]){var k=e;if(e.modes[m].config){k=a.utils.extend(a.utils.clone(e),e.modes[m].config)}var l=new a.embed[e.modes[m].type](document.getElementById(g.id),e.modes[m],k,h,g);if(l.supportsConfig()){l.embed();c(g,e.events);return g}}}a.utils.log("No suitable players found");new a.embed.logo(a.utils.extend({hide:true},e.components.logo),"none",g.id)}}h.addEventListener(a.events.COMPLETE,d);h.addEventListener(a.events.ERROR,d);h.load();return g};function b(){if(!document.body){return setTimeout(b,15)}var c=a.utils.selectors.getElementsByTagAndClass("video","jwplayer");for(var d=0;d<c.length;d++){var e=c[d];a(e.id).setup({})}}b()})(jwplayer);(function(e){function h(){return[{type:"flash",src:"/jwplayer/player.swf"},{type:"html5"},{type:"download"}]}var a={players:"modes",autoplay:"autostart"};function b(n){var m=n.toLowerCase();var l=["left","right","top","bottom"];for(var k=0;k<l.length;k++){if(m==l[k]){return true}}return false}function c(l){var k=false;k=(l instanceof Array)||(typeof l=="object"&&!l.position&&!l.size);return k}function j(k){if(typeof k=="string"){if(parseInt(k).toString()==k||k.toLowerCase().indexOf("px")>-1){return parseInt(k)}}return k}var g=["playlist","dock","controlbar","logo","display"];function f(k){var n={};switch(e.utils.typeOf(k.plugins)){case"object":for(var m in k.plugins){n[e.utils.getPluginName(m)]=m}break;case"string":var o=k.plugins.split(",");for(var l=0;l<o.length;l++){n[e.utils.getPluginName(o[l])]=o[l]}break}return n}function d(o,n,m,k){if(e.utils.typeOf(o[n])!="object"){o[n]={}}var l=o[n][m];if(e.utils.typeOf(l)!="object"){o[n][m]=l={}}if(k){if(n=="plugins"){var p=e.utils.getPluginName(m);l[k]=o[p+"."+k];delete o[p+"."+k]}else{l[k]=o[m+"."+k];delete o[m+"."+k]}}}e.embed.deserialize=function(l){var m=f(l);for(var k in m){d(l,"plugins",m[k])}for(var p in l){if(p.indexOf(".")>-1){var o=p.split(".");var n=o[0];var p=o[1];if(e.utils.isInArray(g,n)){d(l,"components",n,p)}else{if(m[n]){d(l,"plugins",m[n],p)}}}}return l};e.embed.config=function(k,u){var t=e.utils.extend({},k);var r;if(c(t.playlist)){r=t.playlist;delete t.playlist}t=e.embed.deserialize(t);t.height=j(t.height);t.width=j(t.width);if(typeof t.plugins=="string"){var l=t.plugins.split(",");if(typeof t.plugins!="object"){t.plugins={}}for(var p=0;p<l.length;p++){var q=e.utils.getPluginName(l[p]);if(typeof t[q]=="object"){t.plugins[l[p]]=t[q];delete t[q]}else{t.plugins[l[p]]={}}}}for(var s=0;s<g.length;s++){var o=g[s];if(e.utils.exists(t[o])){if(typeof t[o]!="object"){if(!t.components[o]){t.components[o]={}}if(o=="logo"){t.components[o].file=t[o]}else{t.components[o].position=t[o]}delete t[o]}else{if(!t.components[o]){t.components[o]={}}e.utils.extend(t.components[o],t[o]);delete t[o]}}if(typeof t[o+"size"]!="undefined"){if(!t.components[o]){t.components[o]={}}t.components[o].size=t[o+"size"];delete t[o+"size"]}}if(typeof t.icons!="undefined"){if(!t.components.display){t.components.display={}}t.components.display.icons=t.icons;delete t.icons}for(var n in a){if(t[n]){if(!t[a[n]]){t[a[n]]=t[n]}delete t[n]}}var m;if(t.flashplayer&&!t.modes){m=h();m[0].src=t.flashplayer;delete t.flashplayer}else{if(t.modes){if(typeof t.modes=="string"){m=h();m[0].src=t.modes}else{if(t.modes instanceof Array){m=t.modes}else{if(typeof t.modes=="object"&&t.modes.type){m=[t.modes]}}}delete t.modes}else{m=h()}}t.modes=m;if(r){t.playlist=r}return t}})(jwplayer);(function(a){a.embed.download=function(c,g,b,d,f){this.embed=function(){var k=a.utils.extend({},b);var q={};var j=b.width?b.width:480;if(typeof j!="number"){j=parseInt(j,10)}var m=b.height?b.height:320;if(typeof m!="number"){m=parseInt(m,10)}var u,o,n;var s={};if(b.playlist&&b.playlist.length){s.file=b.playlist[0].file;o=b.playlist[0].image;s.levels=b.playlist[0].levels}else{s.file=b.file;o=b.image;s.levels=b.levels}if(s.file){u=s.file}else{if(s.levels&&s.levels.length){u=s.levels[0].file}}n=u?"pointer":"auto";var l={display:{style:{cursor:n,width:j,height:m,backgroundColor:"#000",position:"relative",textDecoration:"none",border:"none",display:"block"}},display_icon:{style:{cursor:n,position:"absolute",display:u?"block":"none",top:0,left:0,border:0,margin:0,padding:0,zIndex:3,width:50,height:50,backgroundImage:"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALdJREFUeNrs18ENgjAYhmFouDOCcQJGcARHgE10BDcgTOIosAGwQOuPwaQeuFRi2p/3Sb6EC5L3QCxZBgAAAOCorLW1zMn65TrlkH4NcV7QNcUQt7Gn7KIhxA+qNIR81spOGkL8oFJDyLJRdosqKDDkK+iX5+d7huzwM40xptMQMkjIOeRGo+VkEVvIPfTGIpKASfYIfT9iCHkHrBEzf4gcUQ56aEzuGK/mw0rHpy4AAACAf3kJMACBxjAQNRckhwAAAABJRU5ErkJggg==)"}},display_iconBackground:{style:{cursor:n,position:"absolute",display:u?"block":"none",top:((m-50)/2),left:((j-50)/2),border:0,width:50,height:50,margin:0,padding:0,zIndex:2,backgroundImage:"url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEpJREFUeNrszwENADAIA7DhX8ENoBMZ5KR10EryckCJiIiIiIiIiIiIiIiIiIiIiIh8GmkRERERERERERERERERERERERGRHSPAAPlXH1phYpYaAAAAAElFTkSuQmCC)"}},display_image:{style:{width:j,height:m,display:o?"block":"none",position:"absolute",cursor:n,left:0,top:0,margin:0,padding:0,textDecoration:"none",zIndex:1,border:"none"}}};var h=function(v,x,y){var w=document.createElement(v);if(y){w.id=y}else{w.id=c.id+"_jwplayer_"+x}a.utils.css(w,l[x].style);return w};q.display=h("a","display",c.id);if(u){q.display.setAttribute("href",a.utils.getAbsolutePath(u))}q.display_image=h("img","display_image");q.display_image.setAttribute("alt","Click to download...");if(o){q.display_image.setAttribute("src",a.utils.getAbsolutePath(o))}if(true){q.display_icon=h("div","display_icon");q.display_iconBackground=h("div","display_iconBackground");q.display.appendChild(q.display_image);q.display_iconBackground.appendChild(q.display_icon);q.display.appendChild(q.display_iconBackground)}_css=a.utils.css;_hide=function(v){_css(v,{display:"none"})};function r(v){_imageWidth=q.display_image.naturalWidth;_imageHeight=q.display_image.naturalHeight;t()}function t(){a.utils.stretch(a.utils.stretching.UNIFORM,q.display_image,j,m,_imageWidth,_imageHeight)}q.display_image.onerror=function(v){_hide(q.display_image)};q.display_image.onload=r;c.parentNode.replaceChild(q.display,c);var p=(b.plugins&&b.plugins.logo)?b.plugins.logo:{};q.display.appendChild(new a.embed.logo(b.components.logo,"download",c.id));f.container=document.getElementById(f.id);f.setPlayer(q.display,"download")};this.supportsConfig=function(){if(b){var j=a.utils.getFirstPlaylistItemFromConfig(b);if(typeof j.file=="undefined"&&typeof j.levels=="undefined"){return true}else{if(j.file){return e(j.file,j.provider,j.playlistfile)}else{if(j.levels&&j.levels.length){for(var h=0;h<j.levels.length;h++){if(j.levels[h].file&&e(j.levels[h].file,j.provider,j.playlistfile)){return true}}}}}}else{return true}};function e(j,l,h){if(h){return false}var k=["image","sound","youtube","http"];if(l&&(k.toString().indexOf(l)>-1)){return true}if(!l||(l&&l=="video")){var m=a.utils.extension(j);if(m&&a.utils.extensionmap[m]){return true}}return false}}})(jwplayer);(function(a){a.embed.flash=function(f,g,l,e,j){function m(o,n,p){var q=document.createElement("param");q.setAttribute("name",n);q.setAttribute("value",p);o.appendChild(q)}function k(o,p,n){return function(q){if(n){document.getElementById(j.id+"_wrapper").appendChild(p)}var s=document.getElementById(j.id).getPluginConfig("display");o.resize(s.width,s.height);var r={left:s.x,top:s.y};a.utils.css(p,r)}}function d(p){if(!p){return{}}var r={};for(var o in p){var n=p[o];for(var q in n){r[o+"."+q]=n[q]}}return r}function h(q,p){if(q[p]){var s=q[p];for(var o in s){var n=s[o];if(typeof n=="string"){if(!q[o]){q[o]=n}}else{for(var r in n){if(!q[o+"."+r]){q[o+"."+r]=n[r]}}}}delete q[p]}}function b(q){if(!q){return{}}var t={},s=[];for(var n in q){var p=a.utils.getPluginName(n);var o=q[n];s.push(n);for(var r in o){t[p+"."+r]=o[r]}}t.plugins=s.join(",");return t}function c(p){var n=p.netstreambasepath?"":"netstreambasepath="+encodeURIComponent(window.location.href.split("#")[0])+"&";for(var o in p){if(typeof(p[o])=="object"){n+=o+"="+encodeURIComponent("[[JSON]]"+a.utils.strings.jsonToString(p[o]))+"&"}else{n+=o+"="+encodeURIComponent(p[o])+"&"}}return n.substring(0,n.length-1)}this.embed=function(){l.id=j.id;var y;var q=a.utils.extend({},l);var n=q.width;var w=q.height;if(f.id+"_wrapper"==f.parentNode.id){y=document.getElementById(f.id+"_wrapper")}else{y=document.createElement("div");y.id=f.id+"_wrapper";a.utils.wrap(f,y);a.utils.css(y,{position:"relative",width:n,height:w})}var o=e.setupPlugins(j,q,k);if(o.length>0){a.utils.extend(q,b(o.plugins))}else{delete q.plugins}var r=["height","width","modes","events"];for(var u=0;u<r.length;u++){delete q[r[u]]}var p="opaque";if(q.wmode){p=q.wmode}h(q,"components");h(q,"providers");if(typeof q["dock.position"]!="undefined"){if(q["dock.position"].toString().toLowerCase()=="false"){q.dock=q["dock.position"];delete q["dock.position"]}}var x="#000000";var t;if(a.utils.isIE()){var v='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" bgcolor="'+x+'" width="100%" height="100%" id="'+f.id+'" name="'+f.id+'" tabindex=0"">';v+='<param name="movie" value="'+g.src+'">';v+='<param name="allowfullscreen" value="true">';v+='<param name="allowscriptaccess" value="always">';v+='<param name="seamlesstabbing" value="true">';v+='<param name="wmode" value="'+p+'">';v+='<param name="flashvars" value="'+c(q)+'">';v+="</object>";a.utils.setOuterHTML(f,v);t=document.getElementById(f.id)}else{var s=document.createElement("object");s.setAttribute("type","application/x-shockwave-flash");s.setAttribute("data",g.src);s.setAttribute("width","100%");s.setAttribute("height","100%");s.setAttribute("bgcolor","#000000");s.setAttribute("id",f.id);s.setAttribute("name",f.id);s.setAttribute("tabindex",0);m(s,"allowfullscreen","true");m(s,"allowscriptaccess","always");m(s,"seamlesstabbing","true");m(s,"wmode",p);m(s,"flashvars",c(q));f.parentNode.replaceChild(s,f);t=s}j.container=t;j.setPlayer(t,"flash")};this.supportsConfig=function(){if(a.utils.hasFlash()){if(l){var o=a.utils.getFirstPlaylistItemFromConfig(l);if(typeof o.file=="undefined"&&typeof o.levels=="undefined"){return true}else{if(o.file){return flashCanPlay(o.file,o.provider)}else{if(o.levels&&o.levels.length){for(var n=0;n<o.levels.length;n++){if(o.levels[n].file&&flashCanPlay(o.levels[n].file,o.provider)){return true}}}}}}else{return true}}return false};flashCanPlay=function(n,p){var o=["video","http","sound","image"];if(p&&(o.toString().indexOf(p<0))){return true}var q=a.utils.extension(n);if(!q){return true}if(a.utils.exists(a.utils.extensionmap[q])&&!a.utils.exists(a.utils.extensionmap[q].flash)){return false}return true}}})(jwplayer);(function(a){a.embed.html5=function(c,g,b,d,f){function e(j,k,h){return function(l){var m=document.getElementById(c.id+"_displayarea");if(h){m.appendChild(k)}var n=m.style;j.resize(parseInt(n.width.replace("px","")),parseInt(n.height.replace("px","")));k.left=n.left;k.top=n.top}}this.embed=function(){if(a.html5){d.setupPlugins(f,b,e);c.innerHTML="";var j=a.utils.extend({screencolor:"0x000000"},b);var h=["plugins","modes","events"];for(var k=0;k<h.length;k++){delete j[h[k]]}if(j.levels&&!j.sources){j.sources=b.levels}if(j.skin&&j.skin.toLowerCase().indexOf(".zip")>0){j.skin=j.skin.replace(/\.zip/i,".xml")}var l=new (a.html5(c)).setup(j);f.container=document.getElementById(f.id);f.setPlayer(l,"html5")}else{return null}};this.supportsConfig=function(){if(!!a.vid.canPlayType){if(b){var j=a.utils.getFirstPlaylistItemFromConfig(b);if(typeof j.file=="undefined"&&typeof j.levels=="undefined"){return true}else{if(j.file){return html5CanPlay(a.vid,j.file,j.provider,j.playlistfile)}else{if(j.levels&&j.levels.length){for(var h=0;h<j.levels.length;h++){if(j.levels[h].file&&html5CanPlay(a.vid,j.levels[h].file,j.provider,j.playlistfile)){return true}}}}}}else{return true}}return false};html5CanPlay=function(k,j,l,h){if(h){return false}if(l&&l=="youtube"){return true}if(l&&l!="video"&&l!="http"&&l!="sound"){return false}var m=a.utils.extension(j);if(!a.utils.exists(m)||!a.utils.exists(a.utils.extensionmap[m])){return true}if(!a.utils.exists(a.utils.extensionmap[m].html5)){return false}if(a.utils.isLegacyAndroid()&&m.match(/m4v|mp4/)){return true}return browserCanPlay(k,a.utils.extensionmap[m].html5)};browserCanPlay=function(j,h){if(!h){return true}if(j.canPlayType(h)){return true}else{if(h=="audio/mp3"&&navigator.userAgent.match(/safari/i)){return j.canPlayType("audio/mpeg")}else{return false}}}}})(jwplayer);(function(a){a.embed.logo=function(m,l,d){var j={prefix:"http://l.longtailvideo.com/"+l+"/",file:"logo.png",link:"http://www.longtailvideo.com/players/jw-flv-player/",margin:8,out:0.5,over:1,timeout:5,hide:false,position:"bottom-left"};_css=a.utils.css;var b;var h;k();function k(){o();c();f()}function o(){if(j.prefix){var q=a.version.split(/\W/).splice(0,2).join("/");if(j.prefix.indexOf(q)<0){j.prefix+=q+"/"}}h=a.utils.extend({},j)}function p(){var s={border:"none",textDecoration:"none",position:"absolute",cursor:"pointer",zIndex:10};s.display=h.hide?"none":"block";var r=h.position.toLowerCase().split("-");for(var q in r){s[r[q]]=h.margin}return s}function c(){b=document.createElement("img");b.id=d+"_jwplayer_logo";b.style.display="none";b.onload=function(q){_css(b,p());e()};if(!h.file){return}if(h.file.indexOf("http://")===0){b.src=h.file}else{b.src=h.prefix+h.file}}if(!h.file){return}function f(){if(h.link){b.onmouseover=g;b.onmouseout=e;b.onclick=n}else{this.mouseEnabled=false}}function n(q){if(typeof q!="undefined"){q.preventDefault();q.stopPropagation()}if(h.link){window.open(h.link,"_blank")}return}function e(q){if(h.link){b.style.opacity=h.out}return}function g(q){if(h.hide){b.style.opacity=h.over}return}return b}})(jwplayer);(function(a){a.html5=function(b){var c=b;this.setup=function(d){a.utils.extend(this,new a.html5.api(c,d));return this};return this}})(jwplayer);(function(b){var d=b.utils;var c=d.css;b.html5.view=function(r,q,f){var u=r;var n=q;var x=f;var w;var g;var C;var s;var D;var p;var A;function z(){w=document.createElement("div");w.id=n.id;w.className=n.className;_videowrapper=document.createElement("div");_videowrapper.id=w.id+"_video_wrapper";n.id=w.id+"_video";c(w,{position:"relative",height:x.height,width:x.width,padding:0,backgroundColor:E(),zIndex:0});function E(){if(u.skin.getComponentSettings("display")&&u.skin.getComponentSettings("display").backgroundcolor){return u.skin.getComponentSettings("display").backgroundcolor}return parseInt("000000",16)}c(n,{width:x.width,height:x.height,top:0,left:0,zIndex:1,margin:"auto",display:"block"});c(_videowrapper,{overflow:"hidden",position:"absolute",top:0,left:0,bottom:0,right:0});d.wrap(n,w);d.wrap(n,_videowrapper);s=document.createElement("div");s.id=w.id+"_displayarea";w.appendChild(s)}function k(){for(var E=0;E<x.plugins.order.length;E++){var F=x.plugins.order[E];if(d.exists(x.plugins.object[F].getDisplayElement)){x.plugins.object[F].height=d.parseDimension(x.plugins.object[F].getDisplayElement().style.height);x.plugins.object[F].width=d.parseDimension(x.plugins.object[F].getDisplayElement().style.width);x.plugins.config[F].currentPosition=x.plugins.config[F].position}}v()}function m(E){c(s,{display:x.getMedia().hasChrome()?"none":"block"})}function v(F){var H=x.getMedia()?x.getMedia().getDisplayElement():null;if(d.exists(H)){if(A!=H){if(A&&A.parentNode){A.parentNode.replaceChild(H,A)}A=H}for(var E=0;E<x.plugins.order.length;E++){var G=x.plugins.order[E];if(d.exists(x.plugins.object[G].getDisplayElement)){x.plugins.config[G].currentPosition=x.plugins.config[G].position}}}j(x.width,x.height)}this.setup=function(){if(x&&x.getMedia()){n=x.getMedia().getDisplayElement()}z();k();u.jwAddEventListener(b.api.events.JWPLAYER_PLAYER_STATE,m);u.jwAddEventListener(b.api.events.JWPLAYER_MEDIA_LOADED,v);u.jwAddEventListener(b.api.events.JWPLAYER_MEDIA_META,function(){y()});var E;if(d.exists(window.onresize)){E=window.onresize}window.onresize=function(F){if(d.exists(E)){try{E(F)}catch(H){}}if(u.jwGetFullscreen()){var G=document.body.getBoundingClientRect();x.width=Math.abs(G.left)+Math.abs(G.right);x.height=window.innerHeight}j(x.width,x.height)}};function h(E){switch(E.keyCode){case 27:if(u.jwGetFullscreen()){u.jwSetFullscreen(false)}break;case 32:if(u.jwGetState()!=b.api.events.state.IDLE&&u.jwGetState()!=b.api.events.state.PAUSED){u.jwPause()}else{u.jwPlay()}break}}function j(H,E){if(w.style.display=="none"){return}var G=[].concat(x.plugins.order);G.reverse();D=G.length+2;if(!x.fullscreen){x.width=H;x.height=E;g=H;C=E;c(s,{top:0,bottom:0,left:0,right:0,width:H,height:E,position:"relative"});c(w,{height:C,width:g});var F=o(t,G);if(F.length>0){D+=F.length;var J=F.indexOf("playlist"),I=F.indexOf("controlbar");if(J>=0&&I>=0){F[J]=F.splice(I,1,F[J])[0]}o(l,F,true)}}else{if(!(navigator&&navigator.vendor&&navigator.vendor.indexOf("Apple")==0)){o(B,G,true)}}y()}function o(J,G,H){var F=[];for(var E=0;E<G.length;E++){var K=G[E];if(d.exists(x.plugins.object[K].getDisplayElement)){if(x.plugins.config[K].currentPosition!=b.html5.view.positions.NONE){var I=J(K,D--);if(!I){F.push(K)}else{x.plugins.object[K].resize(I.width,I.height);if(H){delete I.width;delete I.height}c(x.plugins.object[K].getDisplayElement(),I)}}else{c(x.plugins.object[K].getDisplayElement(),{display:"none"})}}}return F}function t(F,G){if(d.exists(x.plugins.object[F].getDisplayElement)){if(x.plugins.config[F].position&&a(x.plugins.config[F].position)){if(!d.exists(x.plugins.object[F].getDisplayElement().parentNode)){w.appendChild(x.plugins.object[F].getDisplayElement())}var E=e(F);E.zIndex=G;return E}}return false}function l(G,H){if(!d.exists(x.plugins.object[G].getDisplayElement().parentNode)){s.appendChild(x.plugins.object[G].getDisplayElement())}var E=x.width,F=x.height;if(typeof x.width=="string"&&x.width.lastIndexOf("%")>-1){percentage=parseFloat(x.width.substring(0,x.width.lastIndexOf("%")))/100;E=Math.round(window.innerWidth*percentage)}if(typeof x.height=="string"&&x.height.lastIndexOf("%")>-1){percentage=parseFloat(x.height.substring(0,x.height.lastIndexOf("%")))/100;F=Math.round(window.innerHeight*percentage)}return{position:"absolute",width:(E-d.parseDimension(s.style.left)-d.parseDimension(s.style.right)),height:(F-d.parseDimension(s.style.top)-d.parseDimension(s.style.bottom)),zIndex:H}}function B(E,F){return{position:"fixed",width:x.width,height:x.height,zIndex:F}}function y(){if(!d.exists(x.getMedia())){return}s.style.position="absolute";var H=x.getMedia().getDisplayElement();if(H&&H.tagName.toLowerCase()=="video"){H.style.position="absolute";var E,I;if(s.style.width.toString().lastIndexOf("%")>-1||s.style.width.toString().lastIndexOf("%")>-1){var F=s.getBoundingClientRect();E=Math.abs(F.left)+Math.abs(F.right);I=Math.abs(F.top)+Math.abs(F.bottom)}else{E=d.parseDimension(s.style.width);I=d.parseDimension(s.style.height)}if(H.parentNode){H.parentNode.style.left=s.style.left;H.parentNode.style.top=s.style.top}d.stretch(u.jwGetStretching(),H,E,I,H.videoWidth?H.videoWidth:400,H.videoHeight?H.videoHeight:300)}else{var G=x.plugins.object.display.getDisplayElement();if(G){x.getMedia().resize(d.parseDimension(G.style.width),d.parseDimension(G.style.height))}else{x.getMedia().resize(d.parseDimension(s.style.width),d.parseDimension(s.style.height))}}}function e(F){var G={position:"absolute",margin:0,padding:0,top:null};var E=x.plugins.config[F].currentPosition.toLowerCase();switch(E.toUpperCase()){case b.html5.view.positions.TOP:G.top=d.parseDimension(s.style.top);G.left=d.parseDimension(s.style.left);G.width=g-d.parseDimension(s.style.left)-d.parseDimension(s.style.right);G.height=x.plugins.object[F].height;s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].height+"px";s.style.height=d.parseDimension(s.style.height)-G.height+"px";break;case b.html5.view.positions.RIGHT:G.top=d.parseDimension(s.style.top);G.right=d.parseDimension(s.style.right);G.width=x.plugins.object[F].width;G.height=C-d.parseDimension(s.style.top)-d.parseDimension(s.style.bottom);s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].width+"px";s.style.width=d.parseDimension(s.style.width)-G.width+"px";break;case b.html5.view.positions.BOTTOM:G.bottom=d.parseDimension(s.style.bottom);G.left=d.parseDimension(s.style.left);G.width=g-d.parseDimension(s.style.left)-d.parseDimension(s.style.right);G.height=x.plugins.object[F].height;s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].height+"px";s.style.height=d.parseDimension(s.style.height)-G.height+"px";break;case b.html5.view.positions.LEFT:G.top=d.parseDimension(s.style.top);G.left=d.parseDimension(s.style.left);G.width=x.plugins.object[F].width;G.height=C-d.parseDimension(s.style.top)-d.parseDimension(s.style.bottom);s.style[E]=d.parseDimension(s.style[E])+x.plugins.object[F].width+"px";s.style.width=d.parseDimension(s.style.width)-G.width+"px";break;default:break}return G}this.resize=j;this.fullscreen=function(H){if(navigator&&navigator.vendor&&navigator.vendor.indexOf("Apple")===0){if(x.getMedia().getDisplayElement().webkitSupportsFullscreen){if(H){try{x.getMedia().getDisplayElement().webkitEnterFullscreen()}catch(G){}}else{try{x.getMedia().getDisplayElement().webkitExitFullscreen()}catch(G){}}}}else{if(H){document.onkeydown=h;clearInterval(p);var F=document.body.getBoundingClientRect();x.width=Math.abs(F.left)+Math.abs(F.right);x.height=window.innerHeight;var E={position:"fixed",width:"100%",height:"100%",top:0,left:0,zIndex:2147483000};c(w,E);E.zIndex=1;if(x.getMedia()&&x.getMedia().getDisplayElement()){c(x.getMedia().getDisplayElement(),E)}E.zIndex=2;c(s,E)}else{document.onkeydown="";x.width=g;x.height=C;c(w,{position:"relative",height:x.height,width:x.width,zIndex:0})}j(x.width,x.height)}}};function a(e){return([b.html5.view.positions.TOP,b.html5.view.positions.RIGHT,b.html5.view.positions.BOTTOM,b.html5.view.positions.LEFT].toString().indexOf(e.toUpperCase())>-1)}b.html5.view.positions={TOP:"TOP",RIGHT:"RIGHT",BOTTOM:"BOTTOM",LEFT:"LEFT",OVER:"OVER",NONE:"NONE"}})(jwplayer);(function(a){var b={backgroundcolor:"",margin:10,font:"Arial,sans-serif",fontsize:10,fontcolor:parseInt("000000",16),fontstyle:"normal",fontweight:"bold",buttoncolor:parseInt("ffffff",16),position:a.html5.view.positions.BOTTOM,idlehide:false,layout:{left:{position:"left",elements:[{name:"play",type:"button"},{name:"divider",type:"divider"},{name:"prev",type:"button"},{name:"divider",type:"divider"},{name:"next",type:"button"},{name:"divider",type:"divider"},{name:"elapsed",type:"text"}]},center:{position:"center",elements:[{name:"time",type:"slider"}]},right:{position:"right",elements:[{name:"duration",type:"text"},{name:"blank",type:"button"},{name:"divider",type:"divider"},{name:"mute",type:"button"},{name:"volume",type:"slider"},{name:"divider",type:"divider"},{name:"fullscreen",type:"button"}]}}};_utils=a.utils;_css=_utils.css;_hide=function(c){_css(c,{display:"none"})};_show=function(c){_css(c,{display:"block"})};a.html5.controlbar=function(l,V){var k=l;var D=_utils.extend({},b,k.skin.getComponentSettings("controlbar"),V);if(D.position==a.html5.view.positions.NONE||typeof a.html5.view.positions[D.position]=="undefined"){return}if(_utils.mapLength(k.skin.getComponentLayout("controlbar"))>0){D.layout=k.skin.getComponentLayout("controlbar")}var ac;var P;var ab;var E;var v="none";var g;var j;var ad;var f;var e;var y;var Q={};var p=false;var c={};var Y;var h=false;var o;var d;var S=false;var G=false;var W=new a.html5.eventdispatcher();_utils.extend(this,W);function J(){if(!Y){Y=k.skin.getSkinElement("controlbar","background");if(!Y){Y={width:0,height:0,src:null}}}return Y}function N(){ab=0;E=0;P=0;if(!p){var ak={height:J().height,backgroundColor:D.backgroundcolor};ac=document.createElement("div");ac.id=k.id+"_jwplayer_controlbar";_css(ac,ak)}var aj=(k.skin.getSkinElement("controlbar","capLeft"));var ai=(k.skin.getSkinElement("controlbar","capRight"));if(aj){x("capLeft","left",false,ac)}var al={position:"absolute",height:J().height,left:(aj?aj.width:0),zIndex:0};Z("background",ac,al,"img");if(J().src){Q.background.src=J().src}al.zIndex=1;Z("elements",ac,al);if(ai){x("capRight","right",false,ac)}}this.getDisplayElement=function(){return ac};this.resize=function(ak,ai){_utils.cancelAnimation(ac);document.getElementById(k.id).onmousemove=A;e=ak;y=ai;if(G!=k.jwGetFullscreen()){G=k.jwGetFullscreen();d=undefined}var aj=w();A();I({id:k.id,duration:ad,position:j});u({id:k.id,bufferPercent:f});return aj};this.show=function(){if(h){h=false;_show(ac);T()}};this.hide=function(){if(!h){h=true;_hide(ac);aa()}};function q(){var aj=["timeSlider","volumeSlider","timeSliderRail","volumeSliderRail"];for(var ak in aj){var ai=aj[ak];if(typeof Q[ai]!="undefined"){c[ai]=Q[ai].getBoundingClientRect()}}}function A(ai){if(h){return}if(D.position==a.html5.view.positions.OVER||k.jwGetFullscreen()){clearTimeout(o);switch(k.jwGetState()){case a.api.events.state.PAUSED:case a.api.events.state.IDLE:if(!D.idlehide||_utils.exists(ai)){U()}if(D.idlehide){o=setTimeout(function(){z()},2000)}break;default:if(ai){U()}o=setTimeout(function(){z()},2000);break}}}function z(ai){aa();_utils.cancelAnimation(ac);_utils.fadeTo(ac,0,0.1,1,0)}function U(){T();_utils.cancelAnimation(ac);_utils.fadeTo(ac,1,0,1,0)}function H(ai){return function(){if(S&&d!=ai){d=ai;W.sendEvent(ai,{component:"controlbar",boundingRect:O()})}}}var T=H(a.api.events.JWPLAYER_COMPONENT_SHOW);var aa=H(a.api.events.JWPLAYER_COMPONENT_HIDE);function O(){if(D.position==a.html5.view.positions.OVER||k.jwGetFullscreen()){return _utils.getDimensions(ac)}else{return{x:0,y:0,width:0,height:0}}}function Z(am,al,ak,ai){var aj;if(!p){if(!ai){ai="div"}aj=document.createElement(ai);Q[am]=aj;aj.id=ac.id+"_"+am;al.appendChild(aj)}else{aj=document.getElementById(ac.id+"_"+am)}if(_utils.exists(ak)){_css(aj,ak)}return aj}function M(){ah(D.layout.left);ah(D.layout.right,-1);ah(D.layout.center)}function ah(al,ai){var am=al.position=="right"?"right":"left";var ak=_utils.extend([],al.elements);if(_utils.exists(ai)){ak.reverse()}for(var aj=0;aj<ak.length;aj++){C(ak[aj],am)}}function K(){return P++}function C(am,ao){var al,aj,ak,ai,aq;if(am.type=="divider"){x("divider"+K(),ao,true,undefined,undefined,am.width,am.element);return}switch(am.name){case"play":x("playButton",ao,false);x("pauseButton",ao,true);R("playButton","jwPlay");R("pauseButton","jwPause");break;case"prev":x("prevButton",ao,true);R("prevButton","jwPlaylistPrev");break;case"stop":x("stopButton",ao,true);R("stopButton","jwStop");break;case"next":x("nextButton",ao,true);R("nextButton","jwPlaylistNext");break;case"elapsed":x("elapsedText",ao,true);break;case"time":aj=!_utils.exists(k.skin.getSkinElement("controlbar","timeSliderCapLeft"))?0:k.skin.getSkinElement("controlbar","timeSliderCapLeft").width;ak=!_utils.exists(k.skin.getSkinElement("controlbar","timeSliderCapRight"))?0:k.skin.getSkinElement("controlbar","timeSliderCapRight").width;al=ao=="left"?aj:ak;ai=k.skin.getSkinElement("controlbar","timeSliderRail").width+aj+ak;aq={height:J().height,position:"absolute",top:0,width:ai};aq[ao]=ao=="left"?ab:E;var an=Z("timeSlider",Q.elements,aq);x("timeSliderCapLeft",ao,true,an,ao=="left"?0:al);x("timeSliderRail",ao,false,an,al);x("timeSliderBuffer",ao,false,an,al);x("timeSliderProgress",ao,false,an,al);x("timeSliderThumb",ao,false,an,al);x("timeSliderCapRight",ao,true,an,ao=="right"?0:al);X("time");break;case"fullscreen":x("fullscreenButton",ao,false);x("normalscreenButton",ao,true);R("fullscreenButton","jwSetFullscreen",true);R("normalscreenButton","jwSetFullscreen",false);break;case"volume":aj=!_utils.exists(k.skin.getSkinElement("controlbar","volumeSliderCapLeft"))?0:k.skin.getSkinElement("controlbar","volumeSliderCapLeft").width;ak=!_utils.exists(k.skin.getSkinElement("controlbar","volumeSliderCapRight"))?0:k.skin.getSkinElement("controlbar","volumeSliderCapRight").width;al=ao=="left"?aj:ak;ai=k.skin.getSkinElement("controlbar","volumeSliderRail").width+aj+ak;aq={height:J().height,position:"absolute",top:0,width:ai};aq[ao]=ao=="left"?ab:E;var ap=Z("volumeSlider",Q.elements,aq);x("volumeSliderCapLeft",ao,true,ap,ao=="left"?0:al);x("volumeSliderRail",ao,true,ap,al);x("volumeSliderProgress",ao,false,ap,al);x("volumeSliderCapRight",ao,true,ap,ao=="right"?0:al);X("volume");break;case"mute":x("muteButton",ao,false);x("unmuteButton",ao,true);R("muteButton","jwSetMute",true);R("unmuteButton","jwSetMute",false);break;case"duration":x("durationText",ao,true);break}}function x(al,ao,aj,ar,am,ai,ak){if(_utils.exists(k.skin.getSkinElement("controlbar",al))||al.indexOf("Text")>0||al.indexOf("divider")===0){var an={height:J().height,position:"absolute",display:"block",top:0};if((al.indexOf("next")===0||al.indexOf("prev")===0)&&k.jwGetPlaylist().length<2){aj=false;an.display="none"}var at;if(al.indexOf("Text")>0){al.innerhtml="00:00";an.font=D.fontsize+"px/"+(J().height+1)+"px "+D.font;an.color=D.fontcolor;an.textAlign="center";an.fontWeight=D.fontweight;an.fontStyle=D.fontstyle;an.cursor="default";at=14+3*D.fontsize}else{if(al.indexOf("divider")===0){if(ai){if(!isNaN(parseInt(ai))){at=parseInt(ai)}}else{if(ak){var ap=k.skin.getSkinElement("controlbar",ak);if(ap){an.background="url("+ap.src+") repeat-x center left";at=ap.width}}else{an.background="url("+k.skin.getSkinElement("controlbar","divider").src+") repeat-x center left";at=k.skin.getSkinElement("controlbar","divider").width}}}else{an.background="url("+k.skin.getSkinElement("controlbar",al).src+") repeat-x center left";at=k.skin.getSkinElement("controlbar",al).width}}if(ao=="left"){an.left=isNaN(am)?ab:am;if(aj){ab+=at}}else{if(ao=="right"){an.right=isNaN(am)?E:am;if(aj){E+=at}}}if(_utils.typeOf(ar)=="undefined"){ar=Q.elements}an.width=at;if(p){_css(Q[al],an)}else{var aq=Z(al,ar,an);if(_utils.exists(k.skin.getSkinElement("controlbar",al+"Over"))){aq.onmouseover=function(au){aq.style.backgroundImage=["url(",k.skin.getSkinElement("controlbar",al+"Over").src,")"].join("")};aq.onmouseout=function(au){aq.style.backgroundImage=["url(",k.skin.getSkinElement("controlbar",al).src,")"].join("")}}}}}function F(){k.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,B);k.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_ITEM,s);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_BUFFER,u);k.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,r);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_TIME,I);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_MUTE,ag);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_VOLUME,m);k.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_COMPLETE,L)}function B(){N();M();w();ae()}function s(ai){ad=k.jwGetPlaylist()[ai.index].duration;I({id:k.id,duration:ad,position:0});u({id:k.id,bufferProgress:0})}function ae(){I({id:k.id,duration:k.jwGetDuration(),position:0});u({id:k.id,bufferProgress:0});ag({id:k.id,mute:k.jwGetMute()});r({id:k.id,newstate:a.api.events.state.IDLE});m({id:k.id,volume:k.jwGetVolume()})}function R(ak,al,aj){if(p){return}if(_utils.exists(k.skin.getSkinElement("controlbar",ak))){var ai=Q[ak];if(_utils.exists(ai)){_css(ai,{cursor:"pointer"});if(al=="fullscreen"){ai.onmouseup=function(am){am.stopPropagation();k.jwSetFullscreen(!k.jwGetFullscreen())}}else{ai.onmouseup=function(am){am.stopPropagation();if(_utils.exists(aj)){k[al](aj)}else{k[al]()}}}}}}function X(ai){if(p){return}var aj=Q[ai+"Slider"];_css(Q.elements,{cursor:"pointer"});_css(aj,{cursor:"pointer"});aj.onmousedown=function(ak){v=ai};aj.onmouseup=function(ak){ak.stopPropagation();af(ak.pageX)};aj.onmousemove=function(ak){if(v=="time"){g=true;var al=ak.pageX-c[ai+"Slider"].left-window.pageXOffset;_css(Q.timeSliderThumb,{left:al})}}}function af(aj){g=false;var ai;if(v=="time"){ai=aj-c.timeSliderRail.left+window.pageXOffset;var al=ai/c.timeSliderRail.width*ad;if(al<0){al=0}else{if(al>ad){al=ad-3}}if(k.jwGetState()==a.api.events.state.PAUSED||k.jwGetState()==a.api.events.state.IDLE){k.jwPlay()}k.jwSeek(al)}else{if(v=="volume"){ai=aj-c.volumeSliderRail.left-window.pageXOffset;var ak=Math.round(ai/c.volumeSliderRail.width*100);if(ak<0){ak=0}else{if(ak>100){ak=100}}if(k.jwGetMute()){k.jwSetMute(false)}k.jwSetVolume(ak)}}v="none"}function u(aj){if(_utils.exists(aj.bufferPercent)){f=aj.bufferPercent}if(c.timeSliderRail){var ak=c.timeSliderRail.width;var ai=isNaN(Math.round(ak*f/100))?0:Math.round(ak*f/100);_css(Q.timeSliderBuffer,{width:ai})}}function ag(ai){if(ai.mute){_hide(Q.muteButton);_show(Q.unmuteButton);_hide(Q.volumeSliderProgress)}else{_show(Q.muteButton);_hide(Q.unmuteButton);_show(Q.volumeSliderProgress)}}function r(ai){if(ai.newstate==a.api.events.state.BUFFERING||ai.newstate==a.api.events.state.PLAYING){_show(Q.pauseButton);_hide(Q.playButton)}else{_hide(Q.pauseButton);_show(Q.playButton)}A();if(ai.newstate==a.api.events.state.IDLE){_hide(Q.timeSliderBuffer);_hide(Q.timeSliderProgress);_hide(Q.timeSliderThumb);I({id:k.id,duration:k.jwGetDuration(),position:0})}else{_show(Q.timeSliderBuffer);if(ai.newstate!=a.api.events.state.BUFFERING){_show(Q.timeSliderProgress);_show(Q.timeSliderThumb)}}}function L(ai){u({bufferPercent:0});I(_utils.extend(ai,{position:0,duration:ad}))}function I(al){if(_utils.exists(al.position)){j=al.position}if(_utils.exists(al.duration)){ad=al.duration}var aj=(j===ad===0)?0:j/ad;var am=c.timeSliderRail;if(am){var ai=isNaN(Math.round(am.width*aj))?0:Math.round(am.width*aj);var ak=ai;if(Q.timeSliderProgress){Q.timeSliderProgress.style.width=ai+"px";if(!g){if(Q.timeSliderThumb){Q.timeSliderThumb.style.left=ak+"px"}}}}if(Q.durationText){Q.durationText.innerHTML=_utils.timeFormat(ad)}if(Q.elapsedText){Q.elapsedText.innerHTML=_utils.timeFormat(j)}}function n(){var am,aj;var ak=document.getElementById(ac.id+"_elements");if(!ak){return}var al=ak.childNodes;for(var ai in ak.childNodes){if(isNaN(parseInt(ai,10))){continue}if(al[ai].id.indexOf(ac.id+"_divider")===0&&aj&&aj.id.indexOf(ac.id+"_divider")===0&&al[ai].style.backgroundImage==aj.style.backgroundImage){al[ai].style.display="none"}else{if(al[ai].id.indexOf(ac.id+"_divider")===0&&am&&am.style.display!="none"){al[ai].style.display="block"}}if(al[ai].style.display!="none"){aj=al[ai]}am=al[ai]}}function w(){n();if(k.jwGetFullscreen()){_show(Q.normalscreenButton);_hide(Q.fullscreenButton)}else{_hide(Q.normalscreenButton);_show(Q.fullscreenButton)}var aj={width:e};var ai={};if(D.position==a.html5.view.positions.OVER||k.jwGetFullscreen()){aj.left=D.margin;aj.width-=2*D.margin;aj.top=y-J().height-D.margin;aj.height=J().height}var al=k.skin.getSkinElement("controlbar","capLeft");var ak=k.skin.getSkinElement("controlbar","capRight");ai.left=al?al.width:0;ai.width=aj.width-ai.left-(ak?ak.width:0);var am=!_utils.exists(k.skin.getSkinElement("controlbar","timeSliderCapLeft"))?0:k.skin.getSkinElement("controlbar","timeSliderCapLeft").width;_css(Q.timeSliderRail,{width:(ai.width-ab-E),left:am});if(_utils.exists(Q.timeSliderCapRight)){_css(Q.timeSliderCapRight,{left:am+(ai.width-ab-E)})}_css(ac,aj);_css(Q.elements,ai);_css(Q.background,ai);q();return aj}function m(am){if(_utils.exists(Q.volumeSliderRail)){var ak=isNaN(am.volume/100)?1:am.volume/100;var al=_utils.parseDimension(Q.volumeSliderRail.style.width);var ai=isNaN(Math.round(al*ak))?0:Math.round(al*ak);var an=_utils.parseDimension(Q.volumeSliderRail.style.right);var aj=(!_utils.exists(k.skin.getSkinElement("controlbar","volumeSliderCapLeft")))?0:k.skin.getSkinElement("controlbar","volumeSliderCapLeft").width;_css(Q.volumeSliderProgress,{width:ai,left:aj});if(_utils.exists(Q.volumeSliderCapLeft)){_css(Q.volumeSliderCapLeft,{left:0})}}}function t(){N();M();q();p=true;F();D.idlehide=(D.idlehide.toString().toLowerCase()=="true");if(D.position==a.html5.view.positions.OVER&&D.idlehide){ac.style.opacity=0;S=true}else{setTimeout((function(){S=true;T()}),1)}ae()}t();return this}})(jwplayer);(function(b){var a=["width","height","state","playlist","item","position","buffer","duration","volume","mute","fullscreen"];var c=b.utils;b.html5.controller=function(z,w,h,v){var C=z;var G=h;var g=v;var o=w;var J=true;var e=-1;var A=c.exists(G.config.debug)&&(G.config.debug.toString().toLowerCase()=="console");var m=new b.html5.eventdispatcher(o.id,A);c.extend(this,m);var E=[];var d=false;function r(M){if(d){m.sendEvent(M.type,M)}else{E.push(M)}}function K(M){if(!d){m.sendEvent(b.api.events.JWPLAYER_READY,M);if(b.utils.exists(window.playerReady)){playerReady(M)}if(b.utils.exists(window[h.config.playerReady])){window[h.config.playerReady](M)}while(E.length>0){var O=E.shift();m.sendEvent(O.type,O)}if(h.config.autostart&&!b.utils.isIOS()){t(G.item)}while(p.length>0){var N=p.shift();x(N.method,N.arguments)}d=true}}G.addGlobalListener(r);G.addEventListener(b.api.events.JWPLAYER_MEDIA_BUFFER_FULL,function(){G.getMedia().play()});G.addEventListener(b.api.events.JWPLAYER_MEDIA_TIME,function(M){if(M.position>=G.playlist[G.item].start&&e>=0){G.playlist[G.item].start=e;e=-1}});G.addEventListener(b.api.events.JWPLAYER_MEDIA_COMPLETE,function(M){setTimeout(s,25)});function u(){try{f(G.item);if(G.playlist[G.item].levels[0].file.length>0){if(J||G.state==b.api.events.state.IDLE){G.getMedia().load(G.playlist[G.item]);J=false}else{if(G.state==b.api.events.state.PAUSED){G.getMedia().play()}}}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function I(){try{if(G.playlist[G.item].levels[0].file.length>0){switch(G.state){case b.api.events.state.PLAYING:case b.api.events.state.BUFFERING:G.getMedia().pause();break}}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function D(M){try{if(G.playlist[G.item].levels[0].file.length>0){if(typeof M!="number"){M=parseFloat(M)}switch(G.state){case b.api.events.state.IDLE:if(e<0){e=G.playlist[G.item].start;G.playlist[G.item].start=M}u();break;case b.api.events.state.PLAYING:case b.api.events.state.PAUSED:case b.api.events.state.BUFFERING:G.seek(M);break}}return true}catch(N){m.sendEvent(b.api.events.JWPLAYER_ERROR,N)}return false}function n(M){if(!c.exists(M)){M=true}try{G.getMedia().stop(M);return true}catch(N){m.sendEvent(b.api.events.JWPLAYER_ERROR,N)}return false}function k(){try{if(G.playlist[G.item].levels[0].file.length>0){if(G.config.shuffle){f(y())}else{if(G.item+1==G.playlist.length){f(0)}else{f(G.item+1)}}}if(G.state!=b.api.events.state.IDLE){var N=G.state;G.state=b.api.events.state.IDLE;m.sendEvent(b.api.events.JWPLAYER_PLAYER_STATE,{oldstate:N,newstate:b.api.events.state.IDLE})}u();return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function j(){try{if(G.playlist[G.item].levels[0].file.length>0){if(G.config.shuffle){f(y())}else{if(G.item===0){f(G.playlist.length-1)}else{f(G.item-1)}}}if(G.state!=b.api.events.state.IDLE){var N=G.state;G.state=b.api.events.state.IDLE;m.sendEvent(b.api.events.JWPLAYER_PLAYER_STATE,{oldstate:N,newstate:b.api.events.state.IDLE})}u();return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function y(){var M=null;if(G.playlist.length>1){while(!c.exists(M)){M=Math.floor(Math.random()*G.playlist.length);if(M==G.item){M=null}}}else{M=0}return M}function t(N){if(!G.playlist||!G.playlist[N]){return false}try{if(G.playlist[N].levels[0].file.length>0){var O=G.state;if(O!==b.api.events.state.IDLE){if(G.playlist[G.item].provider==G.playlist[N].provider){n(false)}else{n()}}f(N);u()}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function f(M){if(!G.playlist[M]){return}G.setActiveMediaProvider(G.playlist[M]);if(G.item!=M){G.item=M;J=true;m.sendEvent(b.api.events.JWPLAYER_PLAYLIST_ITEM,{index:M})}}function H(N){try{f(G.item);var O=G.getMedia();switch(typeof(N)){case"number":O.volume(N);break;case"string":O.volume(parseInt(N,10));break}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function q(N){try{f(G.item);var O=G.getMedia();if(typeof N=="undefined"){O.mute(!G.mute)}else{if(N.toString().toLowerCase()=="true"){O.mute(true)}else{O.mute(false)}}return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function l(N,M){try{G.width=N;G.height=M;g.resize(N,M);m.sendEvent(b.api.events.JWPLAYER_RESIZE,{width:G.width,height:G.height});return true}catch(O){m.sendEvent(b.api.events.JWPLAYER_ERROR,O)}return false}function B(N){try{if(typeof N=="undefined"){G.fullscreen=!G.fullscreen;g.fullscreen(!G.fullscreen)}else{if(N.toString().toLowerCase()=="true"){G.fullscreen=true;g.fullscreen(true)}else{G.fullscreen=false;g.fullscreen(false)}}m.sendEvent(b.api.events.JWPLAYER_RESIZE,{width:G.width,height:G.height});m.sendEvent(b.api.events.JWPLAYER_FULLSCREEN,{fullscreen:N});return true}catch(M){m.sendEvent(b.api.events.JWPLAYER_ERROR,M)}return false}function L(M){try{n();G.loadPlaylist(M);f(G.item);return true}catch(N){m.sendEvent(b.api.events.JWPLAYER_ERROR,N)}return false}b.html5.controller.repeatoptions={LIST:"LIST",ALWAYS:"ALWAYS",SINGLE:"SINGLE",NONE:"NONE"};function s(){switch(G.config.repeat.toUpperCase()){case b.html5.controller.repeatoptions.SINGLE:u();break;case b.html5.controller.repeatoptions.ALWAYS:if(G.item==G.playlist.length-1&&!G.config.shuffle){t(0)}else{k()}break;case b.html5.controller.repeatoptions.LIST:if(G.item==G.playlist.length-1&&!G.config.shuffle){n();f(0)}else{k()}break;default:n();break}}var p=[];function F(M){return function(){if(d){x(M,arguments)}else{p.push({method:M,arguments:arguments})}}}function x(O,N){var M=[];for(i=0;i<N.length;i++){M.push(N[i])}O.apply(this,M)}this.play=F(u);this.pause=F(I);this.seek=F(D);this.stop=F(n);this.next=F(k);this.prev=F(j);this.item=F(t);this.setVolume=F(H);this.setMute=F(q);this.resize=F(l);this.setFullscreen=F(B);this.load=F(L);this.playerReady=K}})(jwplayer);(function(a){a.html5.defaultSkin=function(){this.text='<?xml version="1.0" ?><skin author="LongTail Video" name="Five" version="1.0"><settings><setting name="backcolor" value="0xFFFFFF"/><setting name="frontcolor" value="0x000000"/><setting name="lightcolor" value="0x000000"/><setting name="screencolor" value="0x000000"/></settings><components><component name="controlbar"><settings><setting name="margin" value="20"/><setting name="fontsize" value="11"/></settings><elements><element name="background" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAIAAABvFaqvAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFJJREFUeNrslLENwAAIwxLU/09j5AiOgD5hVQzNAVY8JK4qEfHMIKBnd2+BQlBINaiRtL/aV2rdzYBsM6CIONbI1NZENTr3RwdB2PlnJgJ6BRgA4hwu5Qg5iswAAAAASUVORK5CYII="/><element name="capLeft" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD5JREFUeNosi8ENACAMAgnuv14H0Z8asI19XEjhOiKCMmibVgJTUt7V6fe9KXOtSQCfctJHu2q3/ot79hNgANc2OTz9uTCCAAAAAElFTkSuQmCC"/><element name="capRight" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD5JREFUeNosi8ENACAMAgnuv14H0Z8asI19XEjhOiKCMmibVgJTUt7V6fe9KXOtSQCfctJHu2q3/ot79hNgANc2OTz9uTCCAAAAAElFTkSuQmCC"/><element name="divider" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAYCAIAAAC0rgCNAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD5JREFUeNosi8ENACAMAgnuv14H0Z8asI19XEjhOiKCMmibVgJTUt7V6fe9KXOtSQCfctJHu2q3/ot79hNgANc2OTz9uTCCAAAAAElFTkSuQmCC"/><element name="playButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEhJREFUeNpiYqABYBo1dNRQ+hr6H4jvA3E8NS39j4SpZvh/LJig4YxEGEqy3kET+w+AOGFQRhTJhrEQkGcczfujhg4CQwECDADpTRWU/B3wHQAAAABJRU5ErkJggg=="/><element name="pauseButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAChJREFUeNpiYBgFo2DwA0YC8v/R1P4nRu+ooaOGUtnQUTAKhgIACDAAFCwQCfAJ4gwAAAAASUVORK5CYII="/><element name="prevButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEtJREFUeNpiYBgFo2Dog/9QDAPyQHweTYwiQ/2B+D0Wi8g2tB+JTdBQRiIMJVkvEy0iglhDF9Aq9uOpHVEwoE+NJDUKRsFgAAABBgDe2hqZcNNL0AAAAABJRU5ErkJggg=="/><element name="nextButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAYCAYAAAAVibZIAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAElJREFUeNpiYBgFo2Dog/9AfB6I5dHE/lNqKAi/B2J/ahsKw/3EGMpIhKEk66WJoaR6fz61IyqemhEFSlL61ExSo2AUDAYAEGAAiG4hj+5t7M8AAAAASUVORK5CYII="/><element name="timeSliderRail" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADxJREFUeNpiYBgFo2AU0Bwwzluw+D8tLWARFhKiqQ9YuLg4aWsBGxs7bS1gZ6e5BWyjSX0UjIKhDgACDABlYQOGh5pYywAAAABJRU5ErkJggg=="/><element name="timeSliderBuffer" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYBgFo2AU0Bww1jc0/aelBSz8/Pw09QELOzs7bS1gY2OjrQWsrKy09gHraFIfBaNgqAOAAAMAvy0DChXHsZMAAAAASUVORK5CYII="/><element name="timeSliderProgress" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpiYBgFo2AU0BwwAvF/WlrARGsfjFow8BaMglEwCugAAAIMAOHfAQunR+XzAAAAAElFTkSuQmCC"/><element name="timeSliderThumb" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAICAYAAAA870V8AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABZJREFUeNpiZICA/yCCiQEJUJcDEGAAY0gBD1/m7Q0AAAAASUVORK5CYII="/><element name="muteButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAYCAYAAADKx8xXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiYBgFIw3MB+L/5Gj8j6yRiRTFyICJXHfTXyMLAXlGati4YDRFDj8AEGAABk8GSqqS4CoAAAAASUVORK5CYII="/><element name="unmuteButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAYCAYAAADKx8xXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAD1JREFUeNpiYBgFgxz8p7bm+cQa+h8LHy7GhEcjIz4bmAjYykiun/8j0fakGPIfTfPgiSr6aB4FVAcAAQYAWdwR1G1Wd2gAAAAASUVORK5CYII="/><element name="volumeSliderRail" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAYCAYAAADkgu3FAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGpJREFUeNpi/P//PwM9ABMDncCoRYPfIqqDZcuW1UPp/6AUDcNM1DQYKtRAlaAj1mCSLSLXYIIWUctgDItoZfDA5aOoqKhGEANIM9LVR7SymGDQUctikuOIXkFNdhHEOFrDjlpEd4sAAgwAriRMub95fu8AAAAASUVORK5CYII="/><element name="volumeSliderProgress" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAYCAYAAADkgu3FAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFtJREFUeNpi/P//PwM9ABMDncCoRYPfIlqAeij9H5SiYZiqBqPTlFqE02BKLSLaYFItIttgQhZRzWB8FjENiuRJ7aAbsMQwYMl7wDIsWUUQ42gNO2oR3S0CCDAAKhKq6MLLn8oAAAAASUVORK5CYII="/><element name="fullscreenButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAE5JREFUeNpiYBgFo2DQA0YC8v/xqP1PjDlMRDrEgUgxkgHIlfZoriVGjmzLsLFHAW2D6D8eA/9Tw7L/BAwgJE90PvhPpNgoGAVDEQAEGAAMdhTyXcPKcAAAAABJRU5ErkJggg=="/><element name="normalscreenButton" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEZJREFUeNpiYBgFo2DIg/9UUkOUAf8JiFFsyX88fJyAkcQgYMQjNkzBoAgiezyRbE+tFGSPxQJ7auYBmma0UTAKBhgABBgAJAEY6zON61sAAAAASUVORK5CYII="/></elements></component><component name="display"><elements><element name="background" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEpJREFUeNrszwENADAIA7DhX8ENoBMZ5KR10EryckCJiIiIiIiIiIiIiIiIiIiIiIh8GmkRERERERERERERERERERERERGRHSPAAPlXH1phYpYaAAAAAElFTkSuQmCC"/><element name="playIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAALdJREFUeNrs18ENgjAYhmFouDOCcQJGcARHgE10BDcgTOIosAGwQOuPwaQeuFRi2p/3Sb6EC5L3QCxZBgAAAOCorLW1zMn65TrlkH4NcV7QNcUQt7Gn7KIhxA+qNIR81spOGkL8oFJDyLJRdosqKDDkK+iX5+d7huzwM40xptMQMkjIOeRGo+VkEVvIPfTGIpKASfYIfT9iCHkHrBEzf4gcUQ56aEzuGK/mw0rHpy4AAACAf3kJMACBxjAQNRckhwAAAABJRU5ErkJggg=="/><element name="muteIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHJJREFUeNrs1jEOgCAMBVAg7t5/8qaoIy4uoobyXsLCxA+0NCUAAADGUWvdQoQ41x4ixNBB2hBvBskdD3w5ZCkl3+33VqI0kjBBlh9rp+uTcyOP33TnolfsU85XX3yIRpQph8ZQY3wTZtU5AACASA4BBgDHoVuY1/fvOQAAAABJRU5ErkJggg=="/><element name="errorIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWlJREFUeNrsl+1twjAQhsHq/7BBYQLYIBmBDcoGMAIjtBPQTcII2SDtBDBBwrU6pGsUO7YbO470PtKJkz9iH++d4ywWAAAAAABgljRNsyWr2bZzDuJG1rLdZhcMbTjrBCGDyUKsqQLFciJb9bSvuG/WagRVRUVUI6gqy5HVeKWfSgRyJruKIU//TrZTSn2nmlaXThrloi/v9F2STC1W4+Aw5cBzkquRc09bofFNc6YLxEON0VUZS5FPTftO49vMjRsIF3RhOGr7/D/pJw+FKU+q0vDyq8W42jCunDqI3LC5XxNj2wHLU1XjaRnb0Lhykhqhhd8MtSF5J9tbjCv4mXGvKJz/65FF/qJryyaaIvzP2QRxZTX2nTuXjvV/VPFSwyLnW7mpH99yTh1FEVro6JBSd40/pMrRdV8vPtcKl28T2pT8TnFZ4yNosct3Q0io6JfBiz1FlGdqVQH3VHnepAEAAAAAADDzEGAAcTwB10jWgxcAAAAASUVORK5CYII="/><element name="bufferIcon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAuhJREFUeNrsWr9rU1EUznuNGqvFQh1ULOhiBx0KDtIuioO4pJuik3FxFfUPaAV1FTdx0Q5d2g4FFxehTnEpZHFoBy20tCIWtGq0TZP4HfkeHB5N8m6Sl/sa74XDybvv3vvOd8/Pe4lXrVZT3dD8VJc0B8QBcUAcEAfESktHGeR5XtMfqFQq/f92zPe/NbtGlKTdCY30kuxrpMGO94BlQCXs+rbh3ONgA6BlzP1p20d80gEI5hmA2A92Qua1Q2PtAFISM+bvjMG8U+Q7oA3rQGASwrYCU6WpNdLGYbA+Pq5jjXIiwi8EEa2UDbQSaKOIuV+SlkcCrfjY8XTI9EpKGwP0C2kru2hLtHqa4zoXtZRWyvi4CLwv9Opr6Hkn6A9HKgEANsQ1iqC3Ub/vRUk2JgmRkatK36kVrnt0qObunwUdUUMXMWYpakJsO5Am8tAw2GBIgwWA+G2S2dMpiw0gDioQRQJoKhRb1QiDwlHZUABYbaXWsm5ae6loTE4ZDxN4CZar8foVzOJ2iyZ2kWF3t7YIevffaMT5yJ70kQb2fQ1sE5SHr2wazs2wgMxgbsEKEAgxAvZUJbQLBGTSBMgNrncJbA6AljtS/eKDJ0Ez+DmrQEzXS2h1Ck25kAg0IZcUOaydCy4sYnN2fOA+2AP16gNoHALlQ+fwH7XO4CxLenUpgj4xr6ugY2roPMbMx+Xs18m/E8CVEIhxsNeg83XWOAN6grG3lGbk8uE5fr4B/WH3cJw+co/l9nTYsSGYCJ/lY5/qv0thn6nrIWmjeJcPSnWOeY++AkF8tpJHIMAUs/MaBBpj3znZfQo5psY+ZrG4gv5HickjEOymKjEeRpgyST6IuZcTcWbnjcgdPi5ghxciRKsl1lDSsgwA1i8fssonJgzmTSqfGUkCENndNdAL7PS6QQ7ZYISTo+1qq0LEWjTWcvY4isa4z+yfQB+7ooyHVg5RI7/i1Ijn/vnggDggDogD4oC00P4KMACd/juEHOrS4AAAAABJRU5ErkJggg=="/></elements></component><component name="dock"><elements><element name="button" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFBJREFUeNrs0cEJACAQA8Eofu0fu/W6EM5ZSAFDRpKTBs00CQQEBAQEBAQEBAQEBAQEBATkK8iqbY+AgICAgICAgICAgICAgICAgIC86QowAG5PAQzEJ0lKAAAAAElFTkSuQmCC"/></elements></component><component name="playlist"><elements><element name="item" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAIAAAC1nk4lAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHhJREFUeNrs2NEJwCAMBcBYuv/CFuIE9VN47WWCR7iocXR3pdWdGPqqwIoMjYfQeAiNh9B4JHc6MHQVHnjggQceeOCBBx77TifyeOY0iHi8DqIdEY8dD5cL094eePzINB5CO/LwcOTptNB4CP25L4TIbZzpU7UEGAA5wz1uF5rF9AAAAABJRU5ErkJggg=="/><element name="sliderRail" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAA8CAIAAADpFA0BAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADhJREFUeNrsy6ENACAMAMHClp2wYxZLAg5Fcu9e3OjuOKqqfTMzbs14CIZhGIZhGIZhGP4VLwEGAK/BBnVFpB0oAAAAAElFTkSuQmCC"/><element name="sliderThumb" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAA8CAIAAADpFA0BAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNrsy7ENACAMBLE8++8caFFKKiRffU53112SGs3ttOohGIZhGIZhGIZh+Fe8BRgAiaUGde6NOSEAAAAASUVORK5CYII="/></elements></component></components></skin>';this.xml=null;if(window.DOMParser){parser=new DOMParser();this.xml=parser.parseFromString(this.text,"text/xml")}else{this.xml=new ActiveXObject("Microsoft.XMLDOM");this.xml.async="false";this.xml.loadXML(this.text)}return this}})(jwplayer);(function(a){_utils=a.utils;_css=_utils.css;_hide=function(b){_css(b,{display:"none"})};_show=function(b){_css(b,{display:"block"})};a.html5.display=function(k,G){var j={icons:true,showmute:false};var Q=_utils.extend({},j,G);var h=k;var P={};var e;var u;var w;var N;var s;var I;var A;var J=!_utils.exists(h.skin.getComponentSettings("display").bufferrotation)?15:parseInt(h.skin.getComponentSettings("display").bufferrotation,10);var q=!_utils.exists(h.skin.getComponentSettings("display").bufferinterval)?100:parseInt(h.skin.getComponentSettings("display").bufferinterval,10);var z=-1;var t="";var K=true;var d;var g=false;var n=false;var H=new a.html5.eventdispatcher();_utils.extend(this,H);var D={display:{style:{cursor:"pointer",top:0,left:0,overflow:"hidden"},click:m},display_icon:{style:{cursor:"pointer",position:"absolute",top:((h.skin.getSkinElement("display","background").height-h.skin.getSkinElement("display","playIcon").height)/2),left:((h.skin.getSkinElement("display","background").width-h.skin.getSkinElement("display","playIcon").width)/2),border:0,margin:0,padding:0,zIndex:3,display:"none"}},display_iconBackground:{style:{cursor:"pointer",position:"absolute",top:((u-h.skin.getSkinElement("display","background").height)/2),left:((e-h.skin.getSkinElement("display","background").width)/2),border:0,backgroundImage:(["url(",h.skin.getSkinElement("display","background").src,")"]).join(""),width:h.skin.getSkinElement("display","background").width,height:h.skin.getSkinElement("display","background").height,margin:0,padding:0,zIndex:2,display:"none"}},display_image:{style:{display:"none",width:e,height:u,position:"absolute",cursor:"pointer",left:0,top:0,margin:0,padding:0,textDecoration:"none",zIndex:1}},display_text:{style:{zIndex:4,position:"relative",opacity:0.8,backgroundColor:parseInt("000000",16),color:parseInt("ffffff",16),textAlign:"center",fontFamily:"Arial,sans-serif",padding:"0 5px",fontSize:14}}};h.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,p);h.jwAddEventListener(a.api.events.JWPLAYER_MEDIA_MUTE,p);h.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_ITEM,p);h.jwAddEventListener(a.api.events.JWPLAYER_ERROR,o);L();function L(){P.display=C("div","display");P.display_text=C("div","display_text");P.display.appendChild(P.display_text);P.display_image=C("img","display_image");P.display_image.onerror=function(R){_hide(P.display_image)};P.display_image.onload=y;P.display_icon=C("div","display_icon");P.display_iconBackground=C("div","display_iconBackground");P.display.appendChild(P.display_image);P.display_iconBackground.appendChild(P.display_icon);P.display.appendChild(P.display_iconBackground);f();setTimeout((function(){n=true;if(Q.icons.toString()=="true"){F()}}),1)}this.getDisplayElement=function(){return P.display};this.resize=function(S,R){_css(P.display,{width:S,height:R});_css(P.display_text,{width:(S-10),top:((R-P.display_text.getBoundingClientRect().height)/2)});_css(P.display_iconBackground,{top:((R-h.skin.getSkinElement("display","background").height)/2),left:((S-h.skin.getSkinElement("display","background").width)/2)});if(e!=S||u!=R){e=S;u=R;d=undefined;F()}c();p({})};this.show=function(){if(g){g=false;r(h.jwGetState())}};this.hide=function(){if(!g){B();g=true}};function y(R){w=P.display_image.naturalWidth;N=P.display_image.naturalHeight;c()}function c(){_utils.stretch(h.jwGetStretching(),P.display_image,e,u,w,N)}function C(R,T){var S=document.createElement(R);S.id=h.id+"_jwplayer_"+T;_css(S,D[T].style);return S}function f(){for(var R in P){if(_utils.exists(D[R].click)){P[R].onclick=D[R].click}}}function m(R){if(typeof R.preventDefault!="undefined"){R.preventDefault()}else{R.returnValue=false}if(h.jwGetState()!=a.api.events.state.PLAYING){h.jwPlay()}else{h.jwPause()}}function O(R){if(A){B();return}P.display_icon.style.backgroundImage=(["url(",h.skin.getSkinElement("display",R).src,")"]).join("");_css(P.display_icon,{width:h.skin.getSkinElement("display",R).width,height:h.skin.getSkinElement("display",R).height,top:(h.skin.getSkinElement("display","background").height-h.skin.getSkinElement("display",R).height)/2,left:(h.skin.getSkinElement("display","background").width-h.skin.getSkinElement("display",R).width)/2});b();if(_utils.exists(h.skin.getSkinElement("display",R+"Over"))){P.display_icon.onmouseover=function(S){P.display_icon.style.backgroundImage=["url(",h.skin.getSkinElement("display",R+"Over").src,")"].join("")};P.display_icon.onmouseout=function(S){P.display_icon.style.backgroundImage=["url(",h.skin.getSkinElement("display",R).src,")"].join("")}}else{P.display_icon.onmouseover=null;P.display_icon.onmouseout=null}}function B(){if(Q.icons.toString()=="true"){_hide(P.display_icon);_hide(P.display_iconBackground);M()}}function b(){if(!g&&Q.icons.toString()=="true"){_show(P.display_icon);_show(P.display_iconBackground);F()}}function o(R){A=true;B();P.display_text.innerHTML=R.error;_show(P.display_text);P.display_text.style.top=((u-P.display_text.getBoundingClientRect().height)/2)+"px"}function E(){P.display_image.style.display="none"}function p(R){if((R.type==a.api.events.JWPLAYER_PLAYER_STATE||R.type==a.api.events.JWPLAYER_PLAYLIST_ITEM)&&A){A=false;_hide(P.display_text)}var S=h.jwGetState();if(S==t){return}t=S;if(z>=0){clearTimeout(z)}if(K||h.jwGetState()==a.api.events.state.PLAYING||h.jwGetState()==a.api.events.state.PAUSED){r(h.jwGetState())}else{z=setTimeout(l(h.jwGetState()),500)}}function l(R){return(function(){r(R)})}function r(R){if(_utils.exists(I)){clearInterval(I);I=null;_utils.animations.rotate(P.display_icon,0)}switch(R){case a.api.events.state.BUFFERING:if(_utils.isIOS()){E();B()}else{if(h.jwGetPlaylist()[h.jwGetItem()].provider=="sound"){v()}s=0;I=setInterval(function(){s+=J;_utils.animations.rotate(P.display_icon,s%360)},q);O("bufferIcon");K=true}break;case a.api.events.state.PAUSED:if(!_utils.isIOS()){if(h.jwGetPlaylist()[h.jwGetItem()].provider!="sound"){_css(P.display_image,{background:"transparent no-repeat center center"})}O("playIcon");K=true}break;case a.api.events.state.IDLE:if(h.jwGetPlaylist()[h.jwGetItem()]&&h.jwGetPlaylist()[h.jwGetItem()].image){v()}else{E()}O("playIcon");K=true;break;default:if(h.jwGetPlaylist()[h.jwGetItem()]&&h.jwGetPlaylist()[h.jwGetItem()].provider=="sound"){if(_utils.isIOS()){E();K=false}else{v()}}else{E();K=false}if(h.jwGetMute()&&Q.showmute){O("muteIcon")}else{B()}break}z=-1}function v(){if(h.jwGetPlaylist()[h.jwGetItem()]&&h.jwGetPlaylist()[h.jwGetItem()].image){_css(P.display_image,{display:"block"});P.display_image.src=_utils.getAbsolutePath(h.jwGetPlaylist()[h.jwGetItem()].image)}}function x(R){return function(){if(!n){return}if(!g&&d!=R){d=R;H.sendEvent(R,{component:"display",boundingRect:_utils.getDimensions(P.display_iconBackground)})}}}var F=x(a.api.events.JWPLAYER_COMPONENT_SHOW);var M=x(a.api.events.JWPLAYER_COMPONENT_HIDE);return this}})(jwplayer);(function(a){_css=a.utils.css;a.html5.dock=function(p,u){function q(){return{align:a.html5.view.positions.RIGHT}}var k=a.utils.extend({},q(),u);if(k.align=="FALSE"){return}var f={};var s=[];var g;var v;var d=false;var t=false;var e={x:0,y:0,width:0,height:0};var r;var j=new a.html5.eventdispatcher();_utils.extend(this,j);var m=document.createElement("div");m.id=p.id+"_jwplayer_dock";p.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,l);this.getDisplayElement=function(){return m};this.setButton=function(A,x,y,z){if(!x&&f[A]){a.utils.arrays.remove(s,A);m.removeChild(f[A].div);delete f[A]}else{if(x){if(!f[A]){f[A]={}}f[A].handler=x;f[A].outGraphic=y;f[A].overGraphic=z;if(!f[A].div){s.push(A);f[A].div=document.createElement("div");f[A].div.style.position="relative";m.appendChild(f[A].div);f[A].div.appendChild(document.createElement("img"));f[A].div.childNodes[0].style.position="absolute";f[A].div.childNodes[0].style.left=0;f[A].div.childNodes[0].style.top=0;f[A].div.childNodes[0].style.zIndex=10;f[A].div.childNodes[0].style.cursor="pointer";f[A].div.appendChild(document.createElement("img"));f[A].div.childNodes[1].style.position="absolute";f[A].div.childNodes[1].style.left=0;f[A].div.childNodes[1].style.top=0;if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}f[A].div.childNodes[1].style.zIndex=9;f[A].div.childNodes[1].style.cursor="pointer";f[A].div.onmouseover=function(){if(f[A].overGraphic){f[A].div.childNodes[0].src=f[A].overGraphic}if(p.skin.getSkinElement("dock","buttonOver")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","buttonOver").src}};f[A].div.onmouseout=function(){if(f[A].outGraphic){f[A].div.childNodes[0].src=f[A].outGraphic}if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}};if(f[A].overGraphic){f[A].div.childNodes[0].src=f[A].overGraphic}if(f[A].outGraphic){f[A].div.childNodes[0].src=f[A].outGraphic}if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}}if(x){f[A].div.onclick=function(B){B.preventDefault();a(p.id).callback(A);if(f[A].overGraphic){f[A].div.childNodes[0].src=f[A].overGraphic}if(p.skin.getSkinElement("dock","button")){f[A].div.childNodes[1].src=p.skin.getSkinElement("dock","button").src}}}}}h(g,v)};function h(x,J){if(s.length>0){var y=10;var I=y;var F=-1;var G=p.skin.getSkinElement("dock","button").height;var E=p.skin.getSkinElement("dock","button").width;var C=x-E-y;var H,B;if(k.align==a.html5.view.positions.LEFT){F=1;C=y}for(var z=0;z<s.length;z++){var K=Math.floor(I/J);if((I+G+y)>((K+1)*J)){I=((K+1)*J)+y;K=Math.floor(I/J)}var A=f[s[z]].div;A.style.top=(I%J)+"px";A.style.left=(C+(p.skin.getSkinElement("dock","button").width+y)*K*F)+"px";var D={x:a.utils.parseDimension(A.style.left),y:a.utils.parseDimension(A.style.top),width:E,height:G};if(!H||(D.x<=H.x&&D.y<=H.y)){H=D}if(!B||(D.x>=B.x&&D.y>=B.y)){B=D}I+=p.skin.getSkinElement("dock","button").height+y}e={x:H.x,y:H.y,width:B.x-H.x+B.width,height:H.y-B.y+B.height}}if(t!=p.jwGetFullscreen()||g!=x||v!=J){g=x;v=J;t=p.jwGetFullscreen();r=undefined;setTimeout(n,1)}}function b(x){return function(){if(!d&&r!=x&&s.length>0){r=x;j.sendEvent(x,{component:"dock",boundingRect:e})}}}function l(x){if(a.utils.isIOS()){switch(x.newstate){case a.api.events.state.IDLE:o();break;default:c();break}}}var n=b(a.api.events.JWPLAYER_COMPONENT_SHOW);var w=b(a.api.events.JWPLAYER_COMPONENT_HIDE);this.resize=h;var o=function(){_css(m,{display:"block"});if(d){d=false;n()}};var c=function(){_css(m,{display:"none"});if(!d){w();d=true}};this.hide=c;this.show=o;return this}})(jwplayer);(function(a){a.html5.eventdispatcher=function(d,b){var c=new a.events.eventdispatcher(b);a.utils.extend(this,c);this.sendEvent=function(e,f){if(!a.utils.exists(f)){f={}}a.utils.extend(f,{id:d,version:a.version,type:e});c.sendEvent(e,f)}}})(jwplayer);(function(a){var b={prefix:"http://l.longtailvideo.com/html5/",file:"logo.png",link:"http://www.longtailvideo.com/players/jw-flv-player/",margin:8,out:0.5,over:1,timeout:5,hide:true,position:"bottom-left"};_css=a.utils.css;a.html5.logo=function(n,r){var q=n;var u;var d;var t;var h=false;g();function g(){o();c();l()}function o(){if(b.prefix){var v=n.version.split(/\W/).splice(0,2).join("/");if(b.prefix.indexOf(v)<0){b.prefix+=v+"/"}}if(r.position==a.html5.view.positions.OVER){r.position=b.position}d=a.utils.extend({},b)}function c(){t=document.createElement("img");t.id=q.id+"_jwplayer_logo";t.style.display="none";t.onload=function(v){_css(t,k());q.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,j);p()};if(!d.file){return}if(d.file.indexOf("http://")===0){t.src=d.file}else{t.src=d.prefix+d.file}}if(!d.file){return}this.resize=function(w,v){};this.getDisplayElement=function(){return t};function l(){if(d.link){t.onmouseover=f;t.onmouseout=p;t.onclick=s}else{this.mouseEnabled=false}}function s(v){if(typeof v!="undefined"){v.stopPropagation()}if(!h){return}q.jwPause();q.jwSetFullscreen(false);if(d.link){window.open(d.link,"_top")}return}function p(v){if(d.link&&h){t.style.opacity=d.out}return}function f(v){if(d.hide.toString()=="true"&&h){t.style.opacity=d.over}return}function k(){var x={textDecoration:"none",position:"absolute",cursor:"pointer"};x.display=(d.hide.toString()=="true")?"none":"block";var w=d.position.toLowerCase().split("-");for(var v in w){x[w[v]]=d.margin}return x}function m(){if(d.hide.toString()=="true"){t.style.display="block";t.style.opacity=0;a.utils.fadeTo(t,d.out,0.1,parseFloat(t.style.opacity));u=setTimeout(function(){e()},d.timeout*1000)}h=true}function e(){h=false;if(d.hide.toString()=="true"){a.utils.fadeTo(t,0,0.1,parseFloat(t.style.opacity))}}function j(v){if(v.newstate==a.api.events.state.BUFFERING){clearTimeout(u);m()}}return this}})(jwplayer);(function(a){var c={ended:a.api.events.state.IDLE,playing:a.api.events.state.PLAYING,pause:a.api.events.state.PAUSED,buffering:a.api.events.state.BUFFERING};var e=a.utils;var b=e.css;var d=e.isIOS();a.html5.mediavideo=function(h,s){var r={abort:n,canplay:k,canplaythrough:k,durationchange:G,emptied:n,ended:k,error:u,loadeddata:G,loadedmetadata:G,loadstart:k,pause:k,play:n,playing:k,progress:v,ratechange:n,seeked:k,seeking:k,stalled:k,suspend:k,timeupdate:D,volumechange:n,waiting:k,canshowcurrentframe:n,dataunavailable:n,empty:n,load:z,loadedfirstframe:n};var j=new a.html5.eventdispatcher();e.extend(this,j);var y=h,l=s,m,B,A,x,f,H=false,C,p,q;o();this.load=function(J,K){if(typeof K=="undefined"){K=true}x=J;e.empty(m);q=0;if(J.levels&&J.levels.length>0){if(J.levels.length==1){m.src=J.levels[0].file}else{if(m.src){m.removeAttribute("src")}for(var I=0;I<J.levels.length;I++){var L=m.ownerDocument.createElement("source");L.src=J.levels[I].file;m.appendChild(L);q++}}}else{m.src=J.file}if(d){if(J.image){m.poster=J.image}m.controls="controls";m.style.display="block"}C=p=A=false;y.buffer=0;if(!e.exists(J.start)){J.start=0}y.duration=J.duration;j.sendEvent(a.api.events.JWPLAYER_MEDIA_LOADED);if((!d&&J.levels.length==1)||!H){m.load()}H=false;if(K){E(a.api.events.state.BUFFERING);j.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:0});this.play()}};this.play=function(){if(B!=a.api.events.state.PLAYING){t();if(p){E(a.api.events.state.PLAYING)}else{E(a.api.events.state.BUFFERING)}m.play()}};this.pause=function(){m.pause();E(a.api.events.state.PAUSED)};this.seek=function(I){if(!(y.duration<=0||isNaN(y.duration))&&!(y.position<=0||isNaN(y.position))){m.currentTime=I;m.play()}};_stop=this.stop=function(I){if(!e.exists(I)){I=true}g();if(I){m.style.display="none";p=false;var J=navigator.userAgent;if(J.match(/chrome/i)){m.src=undefined}else{if(J.match(/safari/i)){m.removeAttribute("src")}else{m.src=""}}m.removeAttribute("controls");m.removeAttribute("poster");e.empty(m);m.load();H=true;if(m.webkitSupportsFullscreen){try{m.webkitExitFullscreen()}catch(K){}}}E(a.api.events.state.IDLE)};this.fullscreen=function(I){if(I===true){this.resize("100%","100%")}else{this.resize(y.config.width,y.config.height)}};this.resize=function(J,I){if(false){b(l,{width:J,height:I})}j.sendEvent(a.api.events.JWPLAYER_MEDIA_RESIZE,{fullscreen:y.fullscreen,width:J,hieght:I})};this.volume=function(I){if(!d){m.volume=I/100;y.volume=I;j.sendEvent(a.api.events.JWPLAYER_MEDIA_VOLUME,{volume:Math.round(I)})}};this.mute=function(I){if(!d){m.muted=I;y.mute=I;j.sendEvent(a.api.events.JWPLAYER_MEDIA_MUTE,{mute:I})}};this.getDisplayElement=function(){return m};this.hasChrome=function(){return false};function o(){m=document.createElement("video");B=a.api.events.state.IDLE;for(var I in r){m.addEventListener(I,function(J){if(e.exists(J.target.parentNode)){r[J.type](J)}},true)}m.setAttribute("x-webkit-airplay","allow");if(l.parentNode){l.parentNode.replaceChild(m,l)}if(!m.id){m.id=l.id}}function E(I){if(I==a.api.events.state.PAUSED&&B==a.api.events.state.IDLE){return}if(B!=I){var J=B;y.state=B=I;j.sendEvent(a.api.events.JWPLAYER_PLAYER_STATE,{oldstate:J,newstate:I})}}function n(I){}function v(K){var J;if(e.exists(K)&&K.lengthComputable&&K.total){J=K.loaded/K.total*100}else{if(e.exists(m.buffered)&&(m.buffered.length>0)){var I=m.buffered.length-1;if(I>=0){J=m.buffered.end(I)/m.duration*100}}}if(p===false&&B==a.api.events.state.BUFFERING){j.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER_FULL);p=true}if(!C){if(J==100){C=true}if(e.exists(J)&&(J>y.buffer)){y.buffer=Math.round(J);j.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:Math.round(J)})}}}function D(J){if(e.exists(J)&&e.exists(J.target)){if(!isNaN(J.target.duration)&&(isNaN(y.duration)||y.duration<1)){if(J.target.duration==Infinity){y.duration=0}else{y.duration=Math.round(J.target.duration*10)/10}}if(!A&&m.readyState>0){m.style.display="block";E(a.api.events.state.PLAYING)}if(B==a.api.events.state.PLAYING){if(!A&&m.readyState>0){A=true;try{if(m.currentTime<x.start){m.currentTime=x.start}}catch(I){}m.volume=y.volume/100;m.muted=y.mute}y.position=y.duration>0?(Math.round(J.target.currentTime*10)/10):0;j.sendEvent(a.api.events.JWPLAYER_MEDIA_TIME,{position:y.position,duration:y.duration});if(y.position>=y.duration&&(y.position>0||y.duration>0)){w()}}}v(J)}function z(I){}function k(I){if(c[I.type]){if(I.type=="ended"){w()}else{E(c[I.type])}}}function G(I){var J={height:I.target.videoHeight,width:I.target.videoWidth,duration:Math.round(I.target.duration*10)/10};if((y.duration===0||isNaN(y.duration))&&I.target.duration!=Infinity){y.duration=Math.round(I.target.duration*10)/10}j.sendEvent(a.api.events.JWPLAYER_MEDIA_META,{metadata:J})}function u(K){if(B==a.api.events.state.IDLE){return}var J="There was an error: ";if((K.target.error&&K.target.tagName.toLowerCase()=="video")||K.target.parentNode.error&&K.target.parentNode.tagName.toLowerCase()=="video"){var I=!e.exists(K.target.error)?K.target.parentNode.error:K.target.error;switch(I.code){case I.MEDIA_ERR_ABORTED:J="You aborted the video playback: ";break;case I.MEDIA_ERR_NETWORK:J="A network error caused the video download to fail part-way: ";break;case I.MEDIA_ERR_DECODE:J="The video playback was aborted due to a corruption problem or because the video used features your browser did not support: ";break;case I.MEDIA_ERR_SRC_NOT_SUPPORTED:J="The video could not be loaded, either because the server or network failed or because the format is not supported: ";break;default:J="An unknown error occurred: ";break}}else{if(K.target.tagName.toLowerCase()=="source"){q--;if(q>0){return}J="The video could not be loaded, either because the server or network failed or because the format is not supported: "}else{e.log("An unknown error occurred.  Continuing...");return}}_stop(false);J+=F();_error=true;j.sendEvent(a.api.events.JWPLAYER_ERROR,{error:J});return}function F(){var K="";for(var J in x.levels){var I=x.levels[J];var L=l.ownerDocument.createElement("source");K+=a.utils.getAbsolutePath(I.file);if(J<(x.levels.length-1)){K+=", "}}return K}function t(){if(!e.exists(f)){f=setInterval(function(){v()},100)}}function g(){clearInterval(f);f=null}function w(){if(B!=a.api.events.state.IDLE){_stop(false);j.sendEvent(a.api.events.JWPLAYER_MEDIA_COMPLETE)}}}})(jwplayer);(function(a){var c={ended:a.api.events.state.IDLE,playing:a.api.events.state.PLAYING,pause:a.api.events.state.PAUSED,buffering:a.api.events.state.BUFFERING};var b=a.utils.css;a.html5.mediayoutube=function(j,e){var f=new a.html5.eventdispatcher();a.utils.extend(this,f);var l=j;var h=document.getElementById(e.id);var g=a.api.events.state.IDLE;var n,m;function k(p){if(g!=p){var q=g;l.state=p;g=p;f.sendEvent(a.api.events.JWPLAYER_PLAYER_STATE,{oldstate:q,newstate:p})}}this.getDisplayElement=function(){return h};this.play=function(){if(g==a.api.events.state.IDLE){f.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:100});f.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER_FULL);k(a.api.events.state.PLAYING)}else{if(g==a.api.events.state.PAUSED){k(a.api.events.state.PLAYING)}}};this.pause=function(){k(a.api.events.state.PAUSED)};this.seek=function(p){};this.stop=function(p){if(!_utils.exists(p)){p=true}l.position=0;k(a.api.events.state.IDLE);if(p){b(h,{display:"none"})}};this.volume=function(p){l.volume=p;f.sendEvent(a.api.events.JWPLAYER_MEDIA_VOLUME,{volume:Math.round(p)})};this.mute=function(p){h.muted=p;l.mute=p;f.sendEvent(a.api.events.JWPLAYER_MEDIA_MUTE,{mute:p})};this.resize=function(q,p){if(q*p>0&&n){n.width=m.width=q;n.height=m.height=p}f.sendEvent(a.api.events.JWPLAYER_MEDIA_RESIZE,{fullscreen:l.fullscreen,width:q,height:p})};this.fullscreen=function(p){if(p===true){this.resize("100%","100%")}else{this.resize(l.config.width,l.config.height)}};this.load=function(p){o(p);b(n,{display:"block"});k(a.api.events.state.BUFFERING);f.sendEvent(a.api.events.JWPLAYER_MEDIA_BUFFER,{bufferPercent:0});f.sendEvent(a.api.events.JWPLAYER_MEDIA_LOADED);this.play()};this.hasChrome=function(){return(g!=a.api.events.state.IDLE)};function o(v){var s=v.levels[0].file;s=["http://www.youtube.com/v/",d(s),"&amp;hl=en_US&amp;fs=1&autoplay=1"].join("");n=document.createElement("object");n.id=h.id;n.style.position="absolute";var u={movie:s,allowfullscreen:"true",allowscriptaccess:"always"};for(var p in u){var t=document.createElement("param");t.name=p;t.value=u[p];n.appendChild(t)}m=document.createElement("embed");n.appendChild(m);var q={src:s,type:"application/x-shockwave-flash",allowfullscreen:"true",allowscriptaccess:"always",width:n.width,height:n.height};for(var r in q){m.setAttribute(r,q[r])}n.appendChild(m);n.style.zIndex=2147483000;if(h!=n&&h.parentNode){h.parentNode.replaceChild(n,h)}h=n}function d(q){var p=q.split(/\?|\#\!/);var s="";for(var r=0;r<p.length;r++){if(p[r].substr(0,2)=="v="){s=p[r].substr(2)}}if(s==""){if(q.indexOf("/v/")>=0){s=q.substr(q.indexOf("/v/")+3)}else{if(q.indexOf("youtu.be")>=0){s=q.substr(q.indexOf("youtu.be/")+9)}else{s=q}}}if(s.indexOf("?")>-1){s=s.substr(0,s.indexOf("?"))}if(s.indexOf("&")>-1){s=s.substr(0,s.indexOf("&"))}return s}this.embed=m;return this}})(jwplayer);(function(jwplayer){var _configurableStateVariables=["width","height","start","duration","volume","mute","fullscreen","item","plugins","stretching"];jwplayer.html5.model=function(api,container,options){var _api=api;var _container=container;var _model={id:_container.id,playlist:[],state:jwplayer.api.events.state.IDLE,position:0,buffer:0,config:{width:480,height:320,item:-1,skin:undefined,file:undefined,image:undefined,start:0,duration:0,bufferlength:5,volume:90,mute:false,fullscreen:false,repeat:"",stretching:jwplayer.utils.stretching.UNIFORM,autostart:false,debug:undefined,screencolor:undefined}};var _media;var _eventDispatcher=new jwplayer.html5.eventdispatcher();var _components=["display","logo","controlbar","playlist","dock"];jwplayer.utils.extend(_model,_eventDispatcher);for(var option in options){if(typeof options[option]=="string"){var type=/color$/.test(option)?"color":null;options[option]=jwplayer.utils.typechecker(options[option],type)}var config=_model.config;var path=option.split(".");for(var edge in path){if(edge==path.length-1){config[path[edge]]=options[option]}else{if(!jwplayer.utils.exists(config[path[edge]])){config[path[edge]]={}}config=config[path[edge]]}}}for(var index in _configurableStateVariables){var configurableStateVariable=_configurableStateVariables[index];_model[configurableStateVariable]=_model.config[configurableStateVariable]}var pluginorder=_components.concat([]);if(jwplayer.utils.exists(_model.plugins)){if(typeof _model.plugins=="string"){var userplugins=_model.plugins.split(",");for(var userplugin in userplugins){if(typeof userplugins[userplugin]=="string"){pluginorder.push(userplugins[userplugin].replace(/^\s+|\s+$/g,""))}}}}if(jwplayer.utils.isIOS()){pluginorder=["display","logo","dock","playlist"];if(!jwplayer.utils.exists(_model.config.repeat)){_model.config.repeat="list"}}else{if(_model.config.chromeless){pluginorder=["logo","dock","playlist"];if(!jwplayer.utils.exists(_model.config.repeat)){_model.config.repeat="list"}}}_model.plugins={order:pluginorder,config:{},object:{}};if(typeof _model.config.components!="undefined"){for(var component in _model.config.components){_model.plugins.config[component]=_model.config.components[component]}}for(var pluginIndex in _model.plugins.order){var pluginName=_model.plugins.order[pluginIndex];var pluginConfig=!jwplayer.utils.exists(_model.plugins.config[pluginName])?{}:_model.plugins.config[pluginName];_model.plugins.config[pluginName]=!jwplayer.utils.exists(_model.plugins.config[pluginName])?pluginConfig:jwplayer.utils.extend(_model.plugins.config[pluginName],pluginConfig);if(!jwplayer.utils.exists(_model.plugins.config[pluginName].position)){if(pluginName=="playlist"){_model.plugins.config[pluginName].position=jwplayer.html5.view.positions.NONE}else{_model.plugins.config[pluginName].position=jwplayer.html5.view.positions.OVER}}else{_model.plugins.config[pluginName].position=_model.plugins.config[pluginName].position.toString().toUpperCase()}}if(typeof _model.plugins.config.dock!="undefined"){if(typeof _model.plugins.config.dock!="object"){var position=_model.plugins.config.dock.toString().toUpperCase();_model.plugins.config.dock={position:position}}if(typeof _model.plugins.config.dock.position!="undefined"){_model.plugins.config.dock.align=_model.plugins.config.dock.position;_model.plugins.config.dock.position=jwplayer.html5.view.positions.OVER}}function _loadExternal(playlistfile){var loader=new jwplayer.html5.playlistloader();loader.addEventListener(jwplayer.api.events.JWPLAYER_PLAYLIST_LOADED,function(evt){_model.playlist=new jwplayer.html5.playlist(evt);_loadComplete(true)});loader.addEventListener(jwplayer.api.events.JWPLAYER_ERROR,function(evt){_model.playlist=new jwplayer.html5.playlist({playlist:[]});_loadComplete(false)});loader.load(playlistfile)}function _loadComplete(){if(_model.config.shuffle){_model.item=_getShuffleItem()}else{if(_model.config.item>=_model.playlist.length){_model.config.item=_model.playlist.length-1}else{if(_model.config.item<0){_model.config.item=0}}_model.item=_model.config.item}_eventDispatcher.sendEvent(jwplayer.api.events.JWPLAYER_PLAYLIST_LOADED,{playlist:_model.playlist});_eventDispatcher.sendEvent(jwplayer.api.events.JWPLAYER_PLAYLIST_ITEM,{index:_model.item})}_model.loadPlaylist=function(arg){var input;if(typeof arg=="string"){if(arg.indexOf("[")==0||arg.indexOf("{")=="0"){try{input=eval(arg)}catch(err){input=arg}}else{input=arg}}else{input=arg}var config;switch(jwplayer.utils.typeOf(input)){case"object":config=input;break;case"array":config={playlist:input};break;default:_loadExternal(input);return;break}_model.playlist=new jwplayer.html5.playlist(config);if(jwplayer.utils.extension(_model.playlist[0].file)=="xml"){_loadExternal(_model.playlist[0].file)}else{_loadComplete()}};function _getShuffleItem(){var result=null;if(_model.playlist.length>1){while(!jwplayer.utils.exists(result)){result=Math.floor(Math.random()*_model.playlist.length);if(result==_model.item){result=null}}}else{result=0}return result}function forward(evt){if(evt.type==jwplayer.api.events.JWPLAYER_MEDIA_LOADED){_container=_media.getDisplayElement()}_eventDispatcher.sendEvent(evt.type,evt)}var _mediaProviders={};_model.setActiveMediaProvider=function(playlistItem){if(playlistItem.provider=="audio"){playlistItem.provider="sound"}var provider=playlistItem.provider;var current=_media?_media.getDisplayElement():null;if(provider=="sound"||provider=="http"||provider==""){provider="video"}if(!jwplayer.utils.exists(_mediaProviders[provider])){switch(provider){case"video":_media=new jwplayer.html5.mediavideo(_model,current?current:_container);break;case"youtube":_media=new jwplayer.html5.mediayoutube(_model,current?current:_container);break}if(!jwplayer.utils.exists(_media)){return false}_media.addGlobalListener(forward);_mediaProviders[provider]=_media}else{if(_media!=_mediaProviders[provider]){if(_media){_media.stop()}_media=_mediaProviders[provider]}}return true};_model.getMedia=function(){return _media};_model.seek=function(pos){_eventDispatcher.sendEvent(jwplayer.api.events.JWPLAYER_MEDIA_SEEK,{position:_model.position,offset:pos});return _media.seek(pos)};_model.setupPlugins=function(){if(!jwplayer.utils.exists(_model.plugins)||!jwplayer.utils.exists(_model.plugins.order)||_model.plugins.order.length==0){jwplayer.utils.log("No plugins to set up");return _model}for(var i=0;i<_model.plugins.order.length;i++){try{var pluginName=_model.plugins.order[i];if(jwplayer.utils.exists(jwplayer.html5[pluginName])){if(pluginName=="playlist"){_model.plugins.object[pluginName]=new jwplayer.html5.playlistcomponent(_api,_model.plugins.config[pluginName])}else{_model.plugins.object[pluginName]=new jwplayer.html5[pluginName](_api,_model.plugins.config[pluginName])}}else{_model.plugins.order.splice(plugin,plugin+1)}if(typeof _model.plugins.object[pluginName].addGlobalListener=="function"){_model.plugins.object[pluginName].addGlobalListener(forward)}}catch(err){jwplayer.utils.log("Could not setup "+pluginName)}}};return _model}})(jwplayer);(function(a){a.html5.playlist=function(b){var d=[];if(b.playlist&&b.playlist instanceof Array&&b.playlist.length>0){for(var c in b.playlist){if(!isNaN(parseInt(c))){d.push(new a.html5.playlistitem(b.playlist[c]))}}}else{d.push(new a.html5.playlistitem(b))}return d}})(jwplayer);(function(a){var c={size:180,position:a.html5.view.positions.NONE,itemheight:60,thumbs:true,fontcolor:"#000000",overcolor:"",activecolor:"",backgroundcolor:"#f8f8f8",font:"_sans",fontsize:"",fontstyle:"",fontweight:""};var b={_sans:"Arial, Helvetica, sans-serif",_serif:"Times, Times New Roman, serif",_typewriter:"Courier New, Courier, monospace"};_utils=a.utils;_css=_utils.css;_hide=function(d){_css(d,{display:"none"})};_show=function(d){_css(d,{display:"block"})};a.html5.playlistcomponent=function(r,B){var w=r;var e=a.utils.extend({},c,w.skin.getComponentSettings("playlist"),B);if(e.position==a.html5.view.positions.NONE||typeof a.html5.view.positions[e.position]=="undefined"){return}var x;var l;var C;var d;var g;var f;var k=-1;var h={background:undefined,item:undefined,itemOver:undefined,itemImage:undefined,itemActive:undefined};this.getDisplayElement=function(){return x};this.resize=function(F,D){l=F;C=D;if(w.jwGetFullscreen()){_hide(x)}else{var E={display:"block",width:l,height:C};_css(x,E)}};this.show=function(){_show(x)};this.hide=function(){_hide(x)};function j(){x=document.createElement("div");x.id=w.id+"_jwplayer_playlistcomponent";switch(e.position){case a.html5.view.positions.RIGHT:case a.html5.view.positions.LEFT:x.style.width=e.size+"px";break;case a.html5.view.positions.TOP:case a.html5.view.positions.BOTTOM:x.style.height=e.size+"px";break}A();if(h.item){e.itemheight=h.item.height}x.style.backgroundColor="#C6C6C6";w.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,s);w.jwAddEventListener(a.api.events.JWPLAYER_PLAYLIST_ITEM,u);w.jwAddEventListener(a.api.events.JWPLAYER_PLAYER_STATE,m)}function p(){var D=document.createElement("ul");_css(D,{width:x.style.width,minWidth:x.style.width,height:x.style.height,backgroundColor:e.backgroundcolor,backgroundImage:h.background?"url("+h.background.src+")":"",color:e.fontcolor,listStyle:"none",margin:0,padding:0,fontFamily:b[e.font]?b[e.font]:b._sans,fontSize:(e.fontsize?e.fontsize:11)+"px",fontStyle:e.fontstyle,fontWeight:e.fontweight,overflowY:"auto"});return D}function y(D){return function(){var E=f.getElementsByClassName("item")[D];var F=e.fontcolor;var G=h.item?"url("+h.item.src+")":"";if(D==w.jwGetPlaylistIndex()){if(e.activecolor!==""){F=e.activecolor}if(h.itemActive){G="url("+h.itemActive.src+")"}}_css(E,{color:e.overcolor!==""?e.overcolor:F,backgroundImage:h.itemOver?"url("+h.itemOver.src+")":G})}}function o(D){return function(){var E=f.getElementsByClassName("item")[D];var F=e.fontcolor;var G=h.item?"url("+h.item.src+")":"";if(D==w.jwGetPlaylistIndex()){if(e.activecolor!==""){F=e.activecolor}if(h.itemActive){G="url("+h.itemActive.src+")"}}_css(E,{color:F,backgroundImage:G})}}function q(I){var P=d[I];var O=document.createElement("li");O.className="item";_css(O,{height:e.itemheight,display:"block",cursor:"pointer",backgroundImage:h.item?"url("+h.item.src+")":"",backgroundSize:"100% "+e.itemheight+"px"});O.onmouseover=y(I);O.onmouseout=o(I);var J=document.createElement("div");var F=new Image();var K=0;var L=0;var M=0;if(v()&&(P.image||P["playlist.image"]||h.itemImage)){F.className="image";if(h.itemImage){K=(e.itemheight-h.itemImage.height)/2;L=h.itemImage.width;M=h.itemImage.height}else{L=e.itemheight*4/3;M=e.itemheight}_css(J,{height:M,width:L,"float":"left",styleFloat:"left",cssFloat:"left",margin:"0 5px 0 0",background:"black",overflow:"hidden",margin:K+"px",position:"relative"});_css(F,{position:"relative"});J.appendChild(F);F.onload=function(){a.utils.stretch(a.utils.stretching.FILL,F,L,M,this.naturalWidth,this.naturalHeight)};if(P["playlist.image"]){F.src=P["playlist.image"]}else{if(P.image){F.src=P.image}else{if(h.itemImage){F.src=h.itemImage.src}}}O.appendChild(J)}var E=l-L-K*2;if(C<e.itemheight*d.length){E-=15}var D=document.createElement("div");_css(D,{position:"relative",height:"100%",overflow:"hidden"});var G=document.createElement("span");if(P.duration>0){G.className="duration";_css(G,{fontSize:(e.fontsize?e.fontsize:11)+"px",fontWeight:(e.fontweight?e.fontweight:"bold"),width:"40px",height:e.fontsize?e.fontsize+10:20,lineHeight:24,"float":"right",styleFloat:"right",cssFloat:"right"});G.innerHTML=_utils.timeFormat(P.duration);D.appendChild(G)}var N=document.createElement("span");N.className="title";_css(N,{padding:"5px 5px 0 "+(K?0:"5px"),height:e.fontsize?e.fontsize+10:20,lineHeight:e.fontsize?e.fontsize+10:20,overflow:"hidden","float":"left",styleFloat:"left",cssFloat:"left",width:((P.duration>0)?E-50:E)-10+"px",fontSize:(e.fontsize?e.fontsize:13)+"px",fontWeight:(e.fontweight?e.fontweight:"bold")});N.innerHTML=P?P.title:"";D.appendChild(N);if(P.description){var H=document.createElement("span");H.className="description";_css(H,{display:"block","float":"left",styleFloat:"left",cssFloat:"left",margin:0,paddingLeft:N.style.paddingLeft,paddingRight:N.style.paddingRight,lineHeight:(e.fontsize?e.fontsize+4:16)+"px",overflow:"hidden",position:"relative"});H.innerHTML=P.description;D.appendChild(H)}O.appendChild(D);return O}function s(E){x.innerHTML="";d=w.jwGetPlaylist();if(!d){return}items=[];f=p();for(var F=0;F<d.length;F++){var D=q(F);D.onclick=z(F);f.appendChild(D);items.push(D)}k=w.jwGetPlaylistIndex();o(k)();x.appendChild(f);if(_utils.isIOS()&&window.iScroll){f.style.height=e.itemheight*d.length+"px";var G=new iScroll(x.id)}}function z(D){return function(){w.jwPlaylistItem(D);w.jwPlay(true)}}function n(){f.scrollTop=w.jwGetPlaylistIndex()*e.itemheight}function v(){return e.thumbs.toString().toLowerCase()=="true"}function u(D){if(k>=0){o(k)();k=D.index}o(D.index)();n()}function m(){if(e.position==a.html5.view.positions.OVER){switch(w.jwGetState()){case a.api.events.state.IDLE:_show(x);break;default:_hide(x);break}}}function A(){for(var D in h){h[D]=t(D)}}function t(D){return w.skin.getSkinElement("playlist",D)}j();return this}})(jwplayer);(function(b){b.html5.playlistitem=function(d){var e={author:"",date:"",description:"",image:"",link:"",mediaid:"",tags:"",title:"",provider:"",file:"",streamer:"",duration:-1,start:0,currentLevel:-1,levels:[]};var c=b.utils.extend({},e,d);if(c.type){c.provider=c.type;delete c.type}if(c.levels.length===0){c.levels[0]=new b.html5.playlistitemlevel(c)}if(!c.provider){c.provider=a(c.levels[0])}else{c.provider=c.provider.toLowerCase()}return c};function a(e){if(b.utils.isYouTube(e.file)){return"youtube"}else{var f=b.utils.extension(e.file);var c;if(f&&b.utils.extensionmap[f]){if(f=="m3u8"){return"video"}c=b.utils.extensionmap[f].html5}else{if(e.type){c=e.type}}if(c){var d=c.split("/")[0];if(d=="audio"){return"sound"}else{if(d=="video"){return d}}}}return""}})(jwplayer);(function(a){a.html5.playlistitemlevel=function(b){var d={file:"",streamer:"",bitrate:0,width:0};for(var c in d){if(a.utils.exists(b[c])){d[c]=b[c]}}return d}})(jwplayer);(function(a){a.html5.playlistloader=function(){var c=new a.html5.eventdispatcher();a.utils.extend(this,c);this.load=function(e){a.utils.ajax(e,d,b)};function d(g){var f=[];try{var f=a.utils.parsers.rssparser.parse(g.responseXML.firstChild);c.sendEvent(a.api.events.JWPLAYER_PLAYLIST_LOADED,{playlist:new a.html5.playlist({playlist:f})})}catch(h){b("Could not parse the playlist")}}function b(e){c.sendEvent(a.api.events.JWPLAYER_ERROR,{error:e?e:"could not load playlist for whatever reason.  too bad"})}}})(jwplayer);(function(a){a.html5.skin=function(){var b={};var c=false;this.load=function(d,e){new a.html5.skinloader(d,function(f){c=true;b=f;e()},function(){new a.html5.skinloader("",function(f){c=true;b=f;e()})})};this.getSkinElement=function(d,e){if(c){try{return b[d].elements[e]}catch(f){a.utils.log("No such skin component / element: ",[d,e])}}return null};this.getComponentSettings=function(d){if(c){return b[d].settings}return null};this.getComponentLayout=function(d){if(c){return b[d].layout}return null}}})(jwplayer);(function(a){a.html5.skinloader=function(f,p,k){var o={};var c=p;var l=k;var e=true;var j;var n=f;var s=false;function m(){if(typeof n!="string"||n===""){d(a.html5.defaultSkin().xml)}else{a.utils.ajax(a.utils.getAbsolutePath(n),function(t){try{if(a.utils.exists(t.responseXML)){d(t.responseXML);return}}catch(u){h()}d(a.html5.defaultSkin().xml)},function(t){d(a.html5.defaultSkin().xml)})}}function d(y){var E=y.getElementsByTagName("component");if(E.length===0){return}for(var H=0;H<E.length;H++){var C=E[H].getAttribute("name");var B={settings:{},elements:{},layout:{}};o[C]=B;var G=E[H].getElementsByTagName("elements")[0].getElementsByTagName("element");for(var F=0;F<G.length;F++){b(G[F],C)}var z=E[H].getElementsByTagName("settings")[0];if(z&&z.childNodes.length>0){var K=z.getElementsByTagName("setting");for(var P=0;P<K.length;P++){var Q=K[P].getAttribute("name");var I=K[P].getAttribute("value");var x=/color$/.test(Q)?"color":null;o[C].settings[Q]=a.utils.typechecker(I,x)}}var L=E[H].getElementsByTagName("layout")[0];if(L&&L.childNodes.length>0){var M=L.getElementsByTagName("group");for(var w=0;w<M.length;w++){var A=M[w];o[C].layout[A.getAttribute("position")]={elements:[]};for(var O=0;O<A.attributes.length;O++){var D=A.attributes[O];o[C].layout[A.getAttribute("position")][D.name]=D.value}var N=A.getElementsByTagName("*");for(var v=0;v<N.length;v++){var t=N[v];o[C].layout[A.getAttribute("position")].elements.push({type:t.tagName});for(var u=0;u<t.attributes.length;u++){var J=t.attributes[u];o[C].layout[A.getAttribute("position")].elements[v][J.name]=J.value}if(!a.utils.exists(o[C].layout[A.getAttribute("position")].elements[v].name)){o[C].layout[A.getAttribute("position")].elements[v].name=t.tagName}}}}e=false;r()}}function r(){clearInterval(j);if(!s){j=setInterval(function(){q()},100)}}function b(y,x){var w=new Image();var t=y.getAttribute("name");var v=y.getAttribute("src");var A;if(v.indexOf("data:image/png;base64,")===0){A=v}else{var u=a.utils.getAbsolutePath(n);var z=u.substr(0,u.lastIndexOf("/"));A=[z,x,v].join("/")}o[x].elements[t]={height:0,width:0,src:"",ready:false,image:w};w.onload=function(B){g(w,t,x)};w.onerror=function(B){s=true;r();l()};w.src=A}function h(){for(var u in o){var w=o[u];for(var t in w.elements){var x=w.elements[t];var v=x.image;v.onload=null;v.onerror=null;delete x.image;delete w.elements[t]}delete o[u]}}function q(){for(var t in o){if(t!="properties"){for(var u in o[t].elements){if(!o[t].elements[u].ready){return}}}}if(e===false){clearInterval(j);c(o)}}function g(t,v,u){if(o[u]&&o[u].elements[v]){o[u].elements[v].height=t.height;o[u].elements[v].width=t.width;o[u].elements[v].src=t.src;o[u].elements[v].ready=true;r()}else{a.utils.log("Loaded an image for a missing element: "+u+"."+v)}}m()}})(jwplayer);(function(a){a.html5.api=function(c,n){var m={};var f=document.createElement("div");c.parentNode.replaceChild(f,c);f.id=c.id;m.version=a.version;m.id=f.id;var l=new a.html5.model(m,f,n);var j=new a.html5.view(m,f,l);var k=new a.html5.controller(m,f,l,j);m.skin=new a.html5.skin();m.jwPlay=function(o){if(typeof o=="undefined"){e()}else{if(o.toString().toLowerCase()=="true"){k.play()}else{k.pause()}}};m.jwPause=function(o){if(typeof o=="undefined"){e()}else{if(o.toString().toLowerCase()=="true"){k.pause()}else{k.play()}}};function e(){if(l.state==a.api.events.state.PLAYING||l.state==a.api.events.state.BUFFERING){k.pause()}else{k.play()}}m.jwStop=k.stop;m.jwSeek=k.seek;m.jwPlaylistItem=k.item;m.jwPlaylistNext=k.next;m.jwPlaylistPrev=k.prev;m.jwResize=k.resize;m.jwLoad=k.load;function h(o){return function(){return l[o]}}function d(o,q,p){return function(){var r=l.plugins.object[o];if(r&&r[q]&&typeof r[q]=="function"){r[q].apply(r,p)}}}m.jwGetItem=h("item");m.jwGetPosition=h("position");m.jwGetDuration=h("duration");m.jwGetBuffer=h("buffer");m.jwGetWidth=h("width");m.jwGetHeight=h("height");m.jwGetFullscreen=h("fullscreen");m.jwSetFullscreen=k.setFullscreen;m.jwGetVolume=h("volume");m.jwSetVolume=k.setVolume;m.jwGetMute=h("mute");m.jwSetMute=k.setMute;m.jwGetStretching=h("stretching");m.jwGetState=h("state");m.jwGetVersion=function(){return m.version};m.jwGetPlaylist=function(){return l.playlist};m.jwGetPlaylistIndex=m.jwGetItem;m.jwAddEventListener=k.addEventListener;m.jwRemoveEventListener=k.removeEventListener;m.jwSendEvent=k.sendEvent;m.jwDockSetButton=function(r,o,p,q){if(l.plugins.object.dock&&l.plugins.object.dock.setButton){l.plugins.object.dock.setButton(r,o,p,q)}};m.jwControlbarShow=d("controlbar","show");m.jwControlbarHide=d("controlbar","hide");m.jwDockShow=d("dock","show");m.jwDockHide=d("dock","hide");m.jwDisplayShow=d("display","show");m.jwDisplayHide=d("display","hide");m.jwGetLevel=function(){};m.jwGetBandwidth=function(){};m.jwGetLockState=function(){};m.jwLock=function(){};m.jwUnlock=function(){};function b(){if(l.config.playlistfile){l.addEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,g);l.loadPlaylist(l.config.playlistfile)}else{if(typeof l.config.playlist=="string"){l.addEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,g);l.loadPlaylist(l.config.playlist)}else{l.loadPlaylist(l.config);setTimeout(g,25)}}}function g(o){l.removeEventListener(a.api.events.JWPLAYER_PLAYLIST_LOADED,g);l.setupPlugins();j.setup();var o={id:m.id,version:m.version};k.playerReady(o)}if(l.config.chromeless&&!a.utils.isIOS()){b()}else{m.skin.load(l.config.skin,b)}return m}})(jwplayer)};
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/lab.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,514 @@
+/*! LAB.js (LABjs :: Loading And Blocking JavaScript)
+    v2.0.3 (c) Kyle Simpson
+    MIT License
+*/
+
+(function(global){
+	var _$LAB = global.$LAB,
+	
+		// constants for the valid keys of the options object
+		_UseLocalXHR = "UseLocalXHR",
+		_AlwaysPreserveOrder = "AlwaysPreserveOrder",
+		_AllowDuplicates = "AllowDuplicates",
+		_CacheBust = "CacheBust",
+		/*!START_DEBUG*/_Debug = "Debug",/*!END_DEBUG*/
+		_BasePath = "BasePath",
+		
+		// stateless variables used across all $LAB instances
+		root_page = /^[^?#]*\//.exec(location.href)[0],
+		root_domain = /^\w+\:\/\/\/?[^\/]+/.exec(root_page)[0],
+		append_to = document.head || document.getElementsByTagName("head"),
+		
+		// inferences... ick, but still necessary
+		opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") || ("MozAppearance" in document.documentElement.style),
+
+/*!START_DEBUG*/
+		// console.log() and console.error() wrappers
+		log_msg = function(){}, 
+		log_error = log_msg,
+/*!END_DEBUG*/
+		
+		// feature sniffs (yay!)
+		test_script_elem = document.createElement("script"),
+		explicit_preloading = typeof test_script_elem.preload == "boolean", // http://wiki.whatwg.org/wiki/Script_Execution_Control#Proposal_1_.28Nicholas_Zakas.29
+		real_preloading = explicit_preloading || (test_script_elem.readyState && test_script_elem.readyState == "uninitialized"), // will a script preload with `src` set before DOM append?
+		script_ordered_async = !real_preloading && test_script_elem.async === true, // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
+		
+		// XHR preloading (same-domain) and cache-preloading (remote-domain) are the fallbacks (for some browsers)
+		xhr_or_cache_preloading = !real_preloading && !script_ordered_async && !opera_or_gecko
+	;
+
+/*!START_DEBUG*/
+	// define console wrapper functions if applicable
+	if (global.console && global.console.log) {
+		if (!global.console.error) global.console.error = global.console.log;
+		log_msg = function(msg) { global.console.log(msg); };
+		log_error = function(msg,err) { global.console.error(msg,err); };
+	}
+/*!END_DEBUG*/
+
+	// test for function
+	function is_func(func) { return Object.prototype.toString.call(func) == "[object Function]"; }
+
+	// test for array
+	function is_array(arr) { return Object.prototype.toString.call(arr) == "[object Array]"; }
+
+	// make script URL absolute/canonical
+	function canonical_uri(src,base_path) {
+		var absolute_regex = /^\w+\:\/\//;
+		
+		// is `src` is protocol-relative (begins with // or ///), prepend protocol
+		if (/^\/\/\/?/.test(src)) {
+			src = location.protocol + src;
+		}
+		// is `src` page-relative? (not an absolute URL, and not a domain-relative path, beginning with /)
+		else if (!absolute_regex.test(src) && src.charAt(0) != "/") {
+			// prepend `base_path`, if any
+			src = (base_path || "") + src;
+		}
+		// make sure to return `src` as absolute
+		return absolute_regex.test(src) ? src : ((src.charAt(0) == "/" ? root_domain : root_page) + src);
+	}
+
+	// merge `source` into `target`
+	function merge_objs(source,target) {
+		for (var k in source) { if (source.hasOwnProperty(k)) {
+			target[k] = source[k]; // TODO: does this need to be recursive for our purposes?
+		}}
+		return target;
+	}
+
+	// does the chain group have any ready-to-execute scripts?
+	function check_chain_group_scripts_ready(chain_group) {
+		var any_scripts_ready = false;
+		for (var i=0; i<chain_group.scripts.length; i++) {
+			if (chain_group.scripts[i].ready && chain_group.scripts[i].exec_trigger) {
+				any_scripts_ready = true;
+				chain_group.scripts[i].exec_trigger();
+				chain_group.scripts[i].exec_trigger = null;
+			}
+		}
+		return any_scripts_ready;
+	}
+
+	// creates a script load listener
+	function create_script_load_listener(elem,registry_item,flag,onload) {
+		elem.onload = elem.onreadystatechange = function() {
+			if ((elem.readyState && elem.readyState != "complete" && elem.readyState != "loaded") || registry_item[flag]) return;
+			elem.onload = elem.onreadystatechange = null;
+			onload();
+		};
+	}
+
+	// script executed handler
+	function script_executed(registry_item) {
+		registry_item.ready = registry_item.finished = true;
+		for (var i=0; i<registry_item.finished_listeners.length; i++) {
+			registry_item.finished_listeners[i]();
+		}
+		registry_item.ready_listeners = [];
+		registry_item.finished_listeners = [];
+	}
+
+	// make the request for a scriptha
+	function request_script(chain_opts,script_obj,registry_item,onload,preload_this_script) {
+		// setTimeout() "yielding" prevents some weird race/crash conditions in older browsers
+		setTimeout(function(){
+			var script, src = script_obj.real_src, xhr;
+			
+			// don't proceed until `append_to` is ready to append to
+			if ("item" in append_to) { // check if `append_to` ref is still a live node list
+				if (!append_to[0]) { // `append_to` node not yet ready
+					// try again in a little bit -- note: will re-call the anonymous function in the outer setTimeout, not the parent `request_script()`
+					setTimeout(arguments.callee,25);
+					return;
+				}
+				// reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists
+				append_to = append_to[0];
+			}
+			script = document.createElement("script");
+			if (script_obj.type) script.type = script_obj.type;
+			if (script_obj.charset) script.charset = script_obj.charset;
+			
+			// should preloading be used for this script?
+			if (preload_this_script) {
+				// real script preloading?
+				if (real_preloading) {
+					/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload: "+src);/*!END_DEBUG*/
+					registry_item.elem = script;
+					if (explicit_preloading) { // explicit preloading (aka, Zakas' proposal)
+						script.preload = true;
+						script.onpreload = onload;
+					}
+					else {
+						script.onreadystatechange = function(){
+							if (script.readyState == "loaded") onload();
+						};
+					}
+					script.src = src;
+					// NOTE: no append to DOM yet, appending will happen when ready to execute
+				}
+				// same-domain and XHR allowed? use XHR preloading
+				else if (preload_this_script && src.indexOf(root_domain) == 0 && chain_opts[_UseLocalXHR]) {
+					xhr = new XMLHttpRequest(); // note: IE never uses XHR (it supports true preloading), so no more need for ActiveXObject fallback for IE <= 7
+					/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (xhr): "+src);/*!END_DEBUG*/
+					xhr.onreadystatechange = function() {
+						if (xhr.readyState == 4) {
+							xhr.onreadystatechange = function(){}; // fix a memory leak in IE
+							registry_item.text = xhr.responseText + "\n//@ sourceURL=" + src; // http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
+							onload();
+						}
+					};
+					xhr.open("GET",src);
+					xhr.send();
+				}
+				// as a last resort, use cache-preloading
+				else {
+					/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script preload (cache): "+src);/*!END_DEBUG*/
+					script.type = "text/cache-script";
+					create_script_load_listener(script,registry_item,"ready",function() {
+						append_to.removeChild(script);
+						onload();
+					});
+					script.src = src;
+					append_to.insertBefore(script,append_to.firstChild);
+				}
+			}
+			// use async=false for ordered async? parallel-load-serial-execute http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
+			else if (script_ordered_async) {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load (ordered async): "+src);/*!END_DEBUG*/
+				script.async = false;
+				create_script_load_listener(script,registry_item,"finished",onload);
+				script.src = src;
+				append_to.insertBefore(script,append_to.firstChild);
+			}
+			// otherwise, just a normal script element
+			else {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("start script load: "+src);/*!END_DEBUG*/
+				create_script_load_listener(script,registry_item,"finished",onload);
+				script.src = src;
+				append_to.insertBefore(script,append_to.firstChild);
+			}
+		},0);
+	}
+		
+	// create a clean instance of $LAB
+	function create_sandbox() {
+		var global_defaults = {},
+			can_use_preloading = real_preloading || xhr_or_cache_preloading,
+			queue = [],
+			registry = {},
+			instanceAPI
+		;
+		
+		// global defaults
+		global_defaults[_UseLocalXHR] = true;
+		global_defaults[_AlwaysPreserveOrder] = false;
+		global_defaults[_AllowDuplicates] = false;
+		global_defaults[_CacheBust] = false;
+		/*!START_DEBUG*/global_defaults[_Debug] = false;/*!END_DEBUG*/
+		global_defaults[_BasePath] = "";
+
+		// execute a script that has been preloaded already
+		function execute_preloaded_script(chain_opts,script_obj,registry_item) {
+			var script;
+			
+			function preload_execute_finished() {
+				if (script != null) { // make sure this only ever fires once
+					script = null;
+					script_executed(registry_item);
+				}
+			}
+			
+			if (registry[script_obj.src].finished) return;
+			if (!chain_opts[_AllowDuplicates]) registry[script_obj.src].finished = true;
+			
+			script = registry_item.elem || document.createElement("script");
+			if (script_obj.type) script.type = script_obj.type;
+			if (script_obj.charset) script.charset = script_obj.charset;
+			create_script_load_listener(script,registry_item,"finished",preload_execute_finished);
+			
+			// script elem was real-preloaded
+			if (registry_item.elem) {
+				registry_item.elem = null;
+			}
+			// script was XHR preloaded
+			else if (registry_item.text) {
+				script.onload = script.onreadystatechange = null;	// script injection doesn't fire these events
+				script.text = registry_item.text;
+			}
+			// script was cache-preloaded
+			else {
+				script.src = script_obj.real_src;
+			}
+			append_to.insertBefore(script,append_to.firstChild);
+
+			// manually fire execution callback for injected scripts, since events don't fire
+			if (registry_item.text) {
+				preload_execute_finished();
+			}
+		}
+	
+		// process the script request setup
+		function do_script(chain_opts,script_obj,chain_group,preload_this_script) {
+			var registry_item,
+				registry_items,
+				ready_cb = function(){ script_obj.ready_cb(script_obj,function(){ execute_preloaded_script(chain_opts,script_obj,registry_item); }); },
+				finished_cb = function(){ script_obj.finished_cb(script_obj,chain_group); }
+			;
+			
+			script_obj.src = canonical_uri(script_obj.src,chain_opts[_BasePath]);
+			script_obj.real_src = script_obj.src + 
+				// append cache-bust param to URL?
+				(chain_opts[_CacheBust] ? ((/\?.*$/.test(script_obj.src) ? "&_" : "?_") + ~~(Math.random()*1E9) + "=") : "")
+			;
+			
+			if (!registry[script_obj.src]) registry[script_obj.src] = {items:[],finished:false};
+			registry_items = registry[script_obj.src].items;
+
+			// allowing duplicates, or is this the first recorded load of this script?
+			if (chain_opts[_AllowDuplicates] || registry_items.length == 0) {
+				registry_item = registry_items[registry_items.length] = {
+					ready:false,
+					finished:false,
+					ready_listeners:[ready_cb],
+					finished_listeners:[finished_cb]
+				};
+
+				request_script(chain_opts,script_obj,registry_item,
+					// which callback type to pass?
+					(
+					 	(preload_this_script) ? // depends on script-preloading
+						function(){
+							registry_item.ready = true;
+							for (var i=0; i<registry_item.ready_listeners.length; i++) {
+								registry_item.ready_listeners[i]();
+							}
+							registry_item.ready_listeners = [];
+						} :
+						function(){ script_executed(registry_item); }
+					),
+					// signal if script-preloading should be used or not
+					preload_this_script
+				);
+			}
+			else {
+				registry_item = registry_items[0];
+				if (registry_item.finished) {
+					finished_cb();
+				}
+				else {
+					registry_item.finished_listeners.push(finished_cb);
+				}
+			}
+		}
+
+		// creates a closure for each separate chain spawned from this $LAB instance, to keep state cleanly separated between chains
+		function create_chain() {
+			var chainedAPI,
+				chain_opts = merge_objs(global_defaults,{}),
+				chain = [],
+				exec_cursor = 0,
+				scripts_currently_loading = false,
+				group
+			;
+			
+			// called when a script has finished preloading
+			function chain_script_ready(script_obj,exec_trigger) {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script preload finished: "+script_obj.real_src);/*!END_DEBUG*/
+				script_obj.ready = true;
+				script_obj.exec_trigger = exec_trigger;
+				advance_exec_cursor(); // will only check for 'ready' scripts to be executed
+			}
+
+			// called when a script has finished executing
+			function chain_script_executed(script_obj,chain_group) {
+				/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("script execution finished: "+script_obj.real_src);/*!END_DEBUG*/
+				script_obj.ready = script_obj.finished = true;
+				script_obj.exec_trigger = null;
+				// check if chain group is all finished
+				for (var i=0; i<chain_group.scripts.length; i++) {
+					if (!chain_group.scripts[i].finished) return;
+				}
+				// chain_group is all finished if we get this far
+				chain_group.finished = true;
+				advance_exec_cursor();
+			}
+
+			// main driver for executing each part of the chain
+			function advance_exec_cursor() {
+				while (exec_cursor < chain.length) {
+					if (is_func(chain[exec_cursor])) {
+						/*!START_DEBUG*/if (chain_opts[_Debug]) log_msg("$LAB.wait() executing: "+chain[exec_cursor]);/*!END_DEBUG*/
+						try { chain[exec_cursor++](); } catch (err) {
+							/*!START_DEBUG*/if (chain_opts[_Debug]) log_error("$LAB.wait() error caught: ",err);/*!END_DEBUG*/
+						}
+						continue;
+					}
+					else if (!chain[exec_cursor].finished) {
+						if (check_chain_group_scripts_ready(chain[exec_cursor])) continue;
+						break;
+					}
+					exec_cursor++;
+				}
+				// we've reached the end of the chain (so far)
+				if (exec_cursor == chain.length) {
+					scripts_currently_loading = false;
+					group = false;
+				}
+			}
+			
+			// setup next chain script group
+			function init_script_chain_group() {
+				if (!group || !group.scripts) {
+					chain.push(group = {scripts:[],finished:true});
+				}
+			}
+
+			// API for $LAB chains
+			chainedAPI = {
+				// start loading one or more scripts
+				script:function(){
+					for (var i=0; i<arguments.length; i++) {
+						(function(script_obj,script_list){
+							var splice_args;
+							
+							if (!is_array(script_obj)) {
+								script_list = [script_obj];
+							}
+							for (var j=0; j<script_list.length; j++) {
+								init_script_chain_group();
+								script_obj = script_list[j];
+								
+								if (is_func(script_obj)) script_obj = script_obj();
+								if (!script_obj) continue;
+								if (is_array(script_obj)) {
+									// set up an array of arguments to pass to splice()
+									splice_args = [].slice.call(script_obj); // first include the actual array elements we want to splice in
+									splice_args.unshift(j,1); // next, put the `index` and `howMany` parameters onto the beginning of the splice-arguments array
+									[].splice.apply(script_list,splice_args); // use the splice-arguments array as arguments for splice()
+									j--; // adjust `j` to account for the loop's subsequent `j++`, so that the next loop iteration uses the same `j` index value
+									continue;
+								}
+								if (typeof script_obj == "string") script_obj = {src:script_obj};
+								script_obj = merge_objs(script_obj,{
+									ready:false,
+									ready_cb:chain_script_ready,
+									finished:false,
+									finished_cb:chain_script_executed
+								});
+								group.finished = false;
+								group.scripts.push(script_obj);
+								
+								do_script(chain_opts,script_obj,group,(can_use_preloading && scripts_currently_loading));
+								scripts_currently_loading = true;
+								
+								if (chain_opts[_AlwaysPreserveOrder]) chainedAPI.wait();
+							}
+						})(arguments[i],arguments[i]);
+					}
+					return chainedAPI;
+				},
+				// force LABjs to pause in execution at this point in the chain, until the execution thus far finishes, before proceeding
+				wait:function(){
+					if (arguments.length > 0) {
+						for (var i=0; i<arguments.length; i++) {
+							chain.push(arguments[i]);
+						}
+						group = chain[chain.length-1];
+					}
+					else group = false;
+					
+					advance_exec_cursor();
+					
+					return chainedAPI;
+				}
+			};
+
+			// the first chain link API (includes `setOptions` only this first time)
+			return {
+				script:chainedAPI.script, 
+				wait:chainedAPI.wait, 
+				setOptions:function(opts){
+					merge_objs(opts,chain_opts);
+					return chainedAPI;
+				}
+			};
+		}
+
+		// API for each initial $LAB instance (before chaining starts)
+		instanceAPI = {
+			// main API functions
+			setGlobalDefaults:function(opts){
+				merge_objs(opts,global_defaults);
+				return instanceAPI;
+			},
+			setOptions:function(){
+				return create_chain().setOptions.apply(null,arguments);
+			},
+			script:function(){
+				return create_chain().script.apply(null,arguments);
+			},
+			wait:function(){
+				return create_chain().wait.apply(null,arguments);
+			},
+
+			// built-in queuing for $LAB `script()` and `wait()` calls
+			// useful for building up a chain programmatically across various script locations, and simulating
+			// execution of the chain
+			queueScript:function(){
+				queue[queue.length] = {type:"script", args:[].slice.call(arguments)};
+				return instanceAPI;
+			},
+			queueWait:function(){
+				queue[queue.length] = {type:"wait", args:[].slice.call(arguments)};
+				return instanceAPI;
+			},
+			runQueue:function(){
+				var $L = instanceAPI, len=queue.length, i=len, val;
+				for (;--i>=0;) {
+					val = queue.shift();
+					$L = $L[val.type].apply(null,val.args);
+				}
+				return $L;
+			},
+
+			// rollback `[global].$LAB` to what it was before this file was loaded, the return this current instance of $LAB
+			noConflict:function(){
+				global.$LAB = _$LAB;
+				return instanceAPI;
+			},
+
+			// create another clean instance of $LAB
+			sandbox:function(){
+				return create_sandbox();
+			}
+		};
+
+		return instanceAPI;
+	}
+
+	// create the main instance of $LAB
+	global.$LAB = create_sandbox();
+
+
+	/* The following "hack" was suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
+	   NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?).
+	   
+	   The hack essentially "patches" the **page** that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does 
+	   proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked 
+	   document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. 
+	   For instance, jQuery 1.4+ has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or 
+	   fixed by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs.
+	*/ 
+	(function(addEvent,domLoaded,handler){
+		if (document.readyState == null && document[addEvent]){
+			document.readyState = "loading";
+			document[addEvent](domLoaded,handler = function(){
+				document.removeEventListener(domLoaded,handler,false);
+				document.readyState = "complete";
+			},false);
+		}
+	})("addEventListener","DOMContentLoaded");
+
+})(this);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/mustache.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,436 @@
+/*
+  mustache.js — Logic-less templates in JavaScript
+
+  See http://mustache.github.com/ for more info.
+*/
+
+var Mustache = function () {
+  var _toString = Object.prototype.toString;
+
+  Array.isArray = Array.isArray || function (obj) {
+    return _toString.call(obj) == "[object Array]";
+  }
+
+  var _trim = String.prototype.trim, trim;
+
+  if (_trim) {
+    trim = function (text) {
+      return text == null ? "" : _trim.call(text);
+    }
+  } else {
+    var trimLeft, trimRight;
+
+    // IE doesn't match non-breaking spaces with \s.
+    if ((/\S/).test("\xA0")) {
+      trimLeft = /^[\s\xA0]+/;
+      trimRight = /[\s\xA0]+$/;
+    } else {
+      trimLeft = /^\s+/;
+      trimRight = /\s+$/;
+    }
+
+    trim = function (text) {
+      return text == null ? "" :
+        text.toString().replace(trimLeft, "").replace(trimRight, "");
+    }
+  }
+
+  var escapeMap = {
+    "&": "&amp;",
+    "<": "&lt;",
+    ">": "&gt;",
+    '"': '&quot;',
+    "'": '&#39;'
+  };
+
+  function escapeHTML(string) {
+    return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
+      return escapeMap[s] || s;
+    });
+  }
+
+  var regexCache = {};
+  var Renderer = function () {};
+
+  Renderer.prototype = {
+    otag: "{{",
+    ctag: "}}",
+    pragmas: {},
+    buffer: [],
+    pragmas_implemented: {
+      "IMPLICIT-ITERATOR": true
+    },
+    context: {},
+
+    render: function (template, context, partials, in_recursion) {
+      // reset buffer & set context
+      if (!in_recursion) {
+        this.context = context;
+        this.buffer = []; // TODO: make this non-lazy
+      }
+
+      // fail fast
+      if (!this.includes("", template)) {
+        if (in_recursion) {
+          return template;
+        } else {
+          this.send(template);
+          return;
+        }
+      }
+
+      // get the pragmas together
+      template = this.render_pragmas(template);
+
+      // render the template
+      var html = this.render_section(template, context, partials);
+
+      // render_section did not find any sections, we still need to render the tags
+      if (html === false) {
+        html = this.render_tags(template, context, partials, in_recursion);
+      }
+
+      if (in_recursion) {
+        return html;
+      } else {
+        this.sendLines(html);
+      }
+    },
+
+    /*
+      Sends parsed lines
+    */
+    send: function (line) {
+      if (line !== "") {
+        this.buffer.push(line);
+      }
+    },
+
+    sendLines: function (text) {
+      if (text) {
+        var lines = text.split("\n");
+        for (var i = 0; i < lines.length; i++) {
+          this.send(lines[i]);
+        }
+      }
+    },
+
+    /*
+      Looks for %PRAGMAS
+    */
+    render_pragmas: function (template) {
+      // no pragmas
+      if (!this.includes("%", template)) {
+        return template;
+      }
+
+      var that = this;
+      var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) {
+        return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g");
+      });
+
+      return template.replace(regex, function (match, pragma, options) {
+        if (!that.pragmas_implemented[pragma]) {
+          throw({message:
+            "This implementation of mustache doesn't understand the '" +
+            pragma + "' pragma"});
+        }
+        that.pragmas[pragma] = {};
+        if (options) {
+          var opts = options.split("=");
+          that.pragmas[pragma][opts[0]] = opts[1];
+        }
+        return "";
+        // ignore unknown pragmas silently
+      });
+    },
+
+    /*
+      Tries to find a partial in the curent scope and render it
+    */
+    render_partial: function (name, context, partials) {
+      name = trim(name);
+      if (!partials || partials[name] === undefined) {
+        throw({message: "unknown_partial '" + name + "'"});
+      }
+      if (!context || typeof context[name] != "object") {
+        return this.render(partials[name], context, partials, true);
+      }
+      return this.render(partials[name], context[name], partials, true);
+    },
+
+    /*
+      Renders inverted (^) and normal (#) sections
+    */
+    render_section: function (template, context, partials) {
+      if (!this.includes("#", template) && !this.includes("^", template)) {
+        // did not render anything, there were no sections
+        return false;
+      }
+
+      var that = this;
+
+      var regex = this.getCachedRegex("render_section", function (otag, ctag) {
+        // This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder
+        return new RegExp(
+          "^([\\s\\S]*?)" +         // all the crap at the beginning that is not {{*}} ($1)
+
+          otag +                    // {{
+          "(\\^|\\#)\\s*(.+)\\s*" + //  #foo (# == $2, foo == $3)
+          ctag +                    // }}
+
+          "\n*([\\s\\S]*?)" +       // between the tag ($2). leading newlines are dropped
+
+          otag +                    // {{
+          "\\/\\s*\\3\\s*" +        //  /foo (backreference to the opening tag).
+          ctag +                    // }}
+
+          "\\s*([\\s\\S]*)$",       // everything else in the string ($4). leading whitespace is dropped.
+
+        "g");
+      });
+
+
+      // for each {{#foo}}{{/foo}} section do...
+      return template.replace(regex, function (match, before, type, name, content, after) {
+        // before contains only tags, no sections
+        var renderedBefore = before ? that.render_tags(before, context, partials, true) : "",
+
+        // after may contain both sections and tags, so use full rendering function
+            renderedAfter = after ? that.render(after, context, partials, true) : "",
+
+        // will be computed below
+            renderedContent,
+
+            value = that.find(name, context);
+
+        if (type === "^") { // inverted section
+          if (!value || Array.isArray(value) && value.length === 0) {
+            // false or empty list, render it
+            renderedContent = that.render(content, context, partials, true);
+          } else {
+            renderedContent = "";
+          }
+        } else if (type === "#") { // normal section
+          if (Array.isArray(value)) { // Enumerable, Let's loop!
+            renderedContent = that.map(value, function (row) {
+              return that.render(content, that.create_context(row), partials, true);
+            }).join("");
+          } else if (that.is_object(value)) { // Object, Use it as subcontext!
+            renderedContent = that.render(content, that.create_context(value),
+              partials, true);
+          } else if (typeof value == "function") {
+            // higher order section
+            renderedContent = value.call(context, content, function (text) {
+              return that.render(text, context, partials, true);
+            });
+          } else if (value) { // boolean section
+            renderedContent = that.render(content, context, partials, true);
+          } else {
+            renderedContent = "";
+          }
+        }
+
+        return renderedBefore + renderedContent + renderedAfter;
+      });
+    },
+
+    /*
+      Replace {{foo}} and friends with values from our view
+    */
+    render_tags: function (template, context, partials, in_recursion) {
+      // tit for tat
+      var that = this;
+
+      var new_regex = function () {
+        return that.getCachedRegex("render_tags", function (otag, ctag) {
+          return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g");
+        });
+      };
+
+      var regex = new_regex();
+      var tag_replace_callback = function (match, operator, name) {
+        switch(operator) {
+        case "!": // ignore comments
+          return "";
+        case "=": // set new delimiters, rebuild the replace regexp
+          that.set_delimiters(name);
+          regex = new_regex();
+          return "";
+        case ">": // render partial
+          return that.render_partial(name, context, partials);
+        case "{": // the triple mustache is unescaped
+        case "&": // & operator is an alternative unescape method
+          return that.find(name, context);
+        default: // escape the value
+          return escapeHTML(that.find(name, context));
+        }
+      };
+      var lines = template.split("\n");
+      for(var i = 0; i < lines.length; i++) {
+        lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+        if (!in_recursion) {
+          this.send(lines[i]);
+        }
+      }
+
+      if (in_recursion) {
+        return lines.join("\n");
+      }
+    },
+
+    set_delimiters: function (delimiters) {
+      var dels = delimiters.split(" ");
+      this.otag = this.escape_regex(dels[0]);
+      this.ctag = this.escape_regex(dels[1]);
+    },
+
+    escape_regex: function (text) {
+      // thank you Simon Willison
+      if (!arguments.callee.sRE) {
+        var specials = [
+          '/', '.', '*', '+', '?', '|',
+          '(', ')', '[', ']', '{', '}', '\\'
+        ];
+        arguments.callee.sRE = new RegExp(
+          '(\\' + specials.join('|\\') + ')', 'g'
+        );
+      }
+      return text.replace(arguments.callee.sRE, '\\$1');
+    },
+
+    /*
+      find `name` in current `context`. That is find me a value
+      from the view object
+    */
+    find: function (name, context) {
+      name = trim(name);
+
+      // Checks whether a value is thruthy or false or 0
+      function is_kinda_truthy(bool) {
+        return bool === false || bool === 0 || bool;
+      }
+
+      var value;
+
+      // check for dot notation eg. foo.bar
+      if (name.match(/([a-z_]+)\./ig)) {
+        var childValue = this.walk_context(name, context);
+        if (is_kinda_truthy(childValue)) {
+          value = childValue;
+        }
+      } else {
+        if (is_kinda_truthy(context[name])) {
+          value = context[name];
+        } else if (is_kinda_truthy(this.context[name])) {
+          value = this.context[name];
+        }
+      }
+
+      if (typeof value == "function") {
+        return value.apply(context);
+      }
+      if (value !== undefined) {
+        return value;
+      }
+      // silently ignore unkown variables
+      return "";
+    },
+
+    walk_context: function (name, context) {
+      var path = name.split('.');
+      // if the var doesn't exist in current context, check the top level context
+      var value_context = (context[path[0]] != undefined) ? context : this.context;
+      var value = value_context[path.shift()];
+      while (value != undefined && path.length > 0) {
+        value_context = value;
+        value = value[path.shift()];
+      }
+      // if the value is a function, call it, binding the correct context
+      if (typeof value == "function") {
+        return value.apply(value_context);
+      }
+      return value;
+    },
+
+    // Utility methods
+
+    /* includes tag */
+    includes: function (needle, haystack) {
+      return haystack.indexOf(this.otag + needle) != -1;
+    },
+
+    // by @langalex, support for arrays of strings
+    create_context: function (_context) {
+      if (this.is_object(_context)) {
+        return _context;
+      } else {
+        var iterator = ".";
+        if (this.pragmas["IMPLICIT-ITERATOR"]) {
+          iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+        }
+        var ctx = {};
+        ctx[iterator] = _context;
+        return ctx;
+      }
+    },
+
+    is_object: function (a) {
+      return a && typeof a == "object";
+    },
+
+    /*
+      Why, why, why? Because IE. Cry, cry cry.
+    */
+    map: function (array, fn) {
+      if (typeof array.map == "function") {
+        return array.map(fn);
+      } else {
+        var r = [];
+        var l = array.length;
+        for(var i = 0; i < l; i++) {
+          r.push(fn(array[i]));
+        }
+        return r;
+      }
+    },
+
+    getCachedRegex: function (name, generator) {
+      var byOtag = regexCache[this.otag];
+      if (!byOtag) {
+        byOtag = regexCache[this.otag] = {};
+      }
+
+      var byCtag = byOtag[this.ctag];
+      if (!byCtag) {
+        byCtag = byOtag[this.ctag] = {};
+      }
+
+      var regex = byCtag[name];
+      if (!regex) {
+        regex = byCtag[name] = generator(this.otag, this.ctag);
+      }
+
+      return regex;
+    }
+  };
+
+  return({
+    name: "mustache.js",
+    version: "0.5.0-dev",
+
+    /*
+      Turns a template and view into HTML
+    */
+    to_html: function (template, view, partials, send_fun) {
+      var renderer = new Renderer();
+      if (send_fun) {
+        renderer.send = send_fun;
+      }
+      renderer.render(template, view || {}, partials);
+      if (!send_fun) {
+        return renderer.buffer.join("\n");
+      }
+    }
+  });
+}();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/popcorn.code.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,182 @@
+// PLUGIN: Code
+
+(function ( Popcorn ) {
+
+  /**
+   * Code Popcorn Plug-in
+   *
+   * Adds the ability to run arbitrary code (JavaScript functions) according to video timing.
+   *
+   * @param {Object} options
+   *
+   * Required parameters: start, end, template, data, and target.
+   * Optional parameter: static.
+   *
+   *   start: the time in seconds when the mustache template should be rendered
+   *          in the target div.
+   *
+   *   end: the time in seconds when the rendered mustache template should be
+   *        removed from the target div.
+   *
+   *   onStart: the function to be run when the start time is reached.
+   *
+   *   onFrame: [optional] a function to be run on each paint call
+   *            (e.g., called ~60 times per second) between the start and end times.
+   *
+   *   onEnd: [optional] a function to be run when the end time is reached.
+   *
+   * Example:
+     var p = Popcorn('#video')
+
+        // onStart function only
+        .code({
+          start: 1,
+          end: 4,
+          onStart: function( options ) {
+            // called on start
+          }
+        })
+
+        // onStart + onEnd only
+        .code({
+          start: 6,
+          end: 8,
+          onStart: function( options ) {
+            // called on start
+          },
+          onEnd: function ( options ) {
+            // called on end
+          }
+        })
+
+        // onStart, onEnd, onFrame
+        .code({
+          start: 10,
+          end: 14,
+          onStart: function( options ) {
+            // called on start
+          },
+          onFrame: function ( options ) {
+            // called on every paint frame between start and end.
+            // uses mozRequestAnimationFrame, webkitRequestAnimationFrame,
+            // or setTimeout with 16ms window.
+          },
+          onEnd: function ( options ) {
+            // called on end
+          }
+        });
+  *
+  */
+
+  Popcorn.plugin( "code" , function( options ) {
+    var running = false;
+
+    // Setup a proper frame interval function (60fps), favouring paint events.
+    var step = (function() {
+
+      var buildFrameRunner = function( runner ) {
+        return function( f, options ) {
+
+          var _f = function() {
+            running && f();
+            running && runner( _f );
+          };
+
+          _f();
+        };
+      };
+
+      // Figure out which level of browser support we have for this
+      if ( window.webkitRequestAnimationFrame ) {
+        return buildFrameRunner( window.webkitRequestAnimationFrame );
+      } else if ( window.mozRequestAnimationFrame ) {
+        return buildFrameRunner( window.mozRequestAnimationFrame );
+      } else {
+        return buildFrameRunner( function( f ) {
+          window.setTimeout( f, 16 );
+        });
+      }
+
+    })();
+
+    if ( !options.onStart || typeof options.onStart !== "function" ) {
+
+      if ( Popcorn.plugin.debug ) {
+        throw new Error( "Popcorn Code Plugin Error: onStart must be a function." );
+      }
+      options.onStart = Popcorn.nop;
+    }
+
+    if ( options.onEnd && typeof options.onEnd !== "function" ) {
+
+      if ( Popcorn.plugin.debug ) {
+        throw new Error( "Popcorn Code Plugin Error: onEnd  must be a function." );
+      }
+      options.onEnd = undefined;
+    }
+
+    if ( options.onFrame && typeof options.onFrame !== "function" ) {
+
+      if ( Popcorn.plugin.debug ) {
+        throw new Error( "Popcorn Code Plugin Error: onFrame  must be a function." );
+      }
+      options.onFrame = undefined;
+    }
+
+    return {
+      start: function( event, options ) {
+        options.onStart( options );
+
+        if ( options.onFrame ) {
+          running = true;
+          step( options.onFrame, options );
+        }
+      },
+
+      end: function( event, options ) {
+        if ( options.onFrame ) {
+          running = false;
+        }
+
+        if ( options.onEnd ) {
+          options.onEnd( options );
+        }
+      }
+    };
+  },
+  {
+    about: {
+      name: "Popcorn Code Plugin",
+      version: "0.1",
+      author: "David Humphrey (@humphd)",
+      website: "http://vocamus.net/dave"
+    },
+    options: {
+      start: {
+       elem: "input",
+       type: "text",
+       label: "In"
+      },
+      end: {
+        elem: "input",
+        type: "text",
+        label: "Out"
+      },
+      onStart: {
+        elem: "input",
+        type: "function",
+        label: "onStart"
+      },
+      onFrame: {
+        elem: "input",
+        type: "function",
+        label: "onFrame"
+      },
+      onEnd: {
+        elem: "input",
+        type: "function",
+        label: "onEnd"
+      }
+    }
+  });
+})( Popcorn );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/popcorn.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,2191 @@
+(function(global, document) {
+
+  // Popcorn.js does not support archaic browsers
+  if ( !document.addEventListener ) {
+    global.Popcorn = {
+      isSupported: false
+    };
+
+    var methods = ( "forEach extend effects error guid sizeOf isArray nop position disable enable destroy " +
+          "addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " +
+          "timeUpdate plugin removePlugin compose effect parser xhr getJSONP getScript" ).split(/\s+/);
+
+    while ( methods.length ) {
+      global.Popcorn[ methods.shift() ] = function() {};
+    }
+    return;
+  }
+
+  var
+
+  AP = Array.prototype,
+  OP = Object.prototype,
+
+  forEach = AP.forEach,
+  slice = AP.slice,
+  hasOwn = OP.hasOwnProperty,
+  toString = OP.toString,
+
+  // Copy global Popcorn (may not exist)
+  _Popcorn = global.Popcorn,
+
+  //  ID string matching
+  rIdExp  = /^(#([\w\-\_\.]+))$/,
+
+  //  Ready fn cache
+  readyStack = [],
+  readyBound = false,
+  readyFired = false,
+
+  //  Non-public internal data object
+  internal = {
+    events: {
+      hash: {},
+      apis: {}
+    }
+  },
+
+  //  Non-public `requestAnimFrame`
+  //  http://paulirish.com/2011/requestanimationframe-for-smart-animating/
+  requestAnimFrame = (function(){
+    return global.requestAnimationFrame ||
+      global.webkitRequestAnimationFrame ||
+      global.mozRequestAnimationFrame ||
+      global.oRequestAnimationFrame ||
+      global.msRequestAnimationFrame ||
+      function( callback, element ) {
+        global.setTimeout( callback, 16 );
+      };
+  }()),
+
+  refresh = function( obj ) {
+    var currentTime = obj.media.currentTime,
+      animation = obj.options.frameAnimation,
+      disabled = obj.data.disabled,
+      tracks = obj.data.trackEvents,
+      animating = tracks.animating,
+      start = tracks.startIndex,
+      registryByName = Popcorn.registryByName,
+      animIndex = 0,
+      byStart, natives, type;
+
+    start = Math.min( start + 1, tracks.byStart.length - 2 );
+
+    while ( start > 0 && tracks.byStart[ start ] ) {
+
+      byStart = tracks.byStart[ start ];
+      natives = byStart._natives;
+      type = natives && natives.type;
+
+      if ( !natives ||
+          ( !!registryByName[ type ] || !!obj[ type ] ) ) {
+
+        if ( ( byStart.start <= currentTime && byStart.end > currentTime ) &&
+                disabled.indexOf( type ) === -1 ) {
+
+          if ( !byStart._running ) {
+            byStart._running = true;
+            natives.start.call( obj, null, byStart );
+
+            // if the 'frameAnimation' option is used,
+            // push the current byStart object into the `animating` cue
+            if ( animation &&
+                ( byStart && byStart._running && byStart.natives.frame ) ) {
+
+              natives.frame.call( obj, null, byStart, currentTime );
+            }
+          }
+        } else if ( byStart._running === true ) {
+
+          byStart._running = false;
+          natives.end.call( obj, null, byStart );
+
+          if ( animation && byStart._natives.frame ) {
+            animIndex = animating.indexOf( byStart );
+            if ( animIndex >= 0 ) {
+              animating.splice( animIndex, 1 );
+            }
+          }
+        }
+      }
+
+      start--;
+    }
+  },
+
+  //  Declare constructor
+  //  Returns an instance object.
+  Popcorn = function( entity, options ) {
+    //  Return new Popcorn object
+    return new Popcorn.p.init( entity, options || null );
+  };
+
+  //  Popcorn API version, automatically inserted via build system.
+  Popcorn.version = "@VERSION";
+
+  //  Boolean flag allowing a client to determine if Popcorn can be supported
+  Popcorn.isSupported = true;
+
+  //  Instance caching
+  Popcorn.instances = [];
+
+  //  Declare a shortcut (Popcorn.p) to and a definition of
+  //  the new prototype for our Popcorn constructor
+  Popcorn.p = Popcorn.prototype = {
+
+    init: function( entity, options ) {
+
+      var matches;
+
+      //  Supports Popcorn(function () { /../ })
+      //  Originally proposed by Daniel Brooks
+
+      if ( typeof entity === "function" ) {
+
+        //  If document ready has already fired
+        if ( document.readyState === "interactive" || document.readyState === "complete" ) {
+
+          entity( document, Popcorn );
+
+          return;
+        }
+        //  Add `entity` fn to ready stack
+        readyStack.push( entity );
+
+        //  This process should happen once per page load
+        if ( !readyBound ) {
+
+          //  set readyBound flag
+          readyBound = true;
+
+          var DOMContentLoaded  = function() {
+
+            readyFired = true;
+
+            //  Remove global DOM ready listener
+            document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+            //  Execute all ready function in the stack
+            for ( var i = 0, readyStackLength = readyStack.length; i < readyStackLength; i++ ) {
+
+              readyStack[ i ].call( document, Popcorn );
+
+            }
+            //  GC readyStack
+            readyStack = null;
+          };
+
+          //  Register global DOM ready listener
+          document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+        }
+
+        return;
+      }
+
+      //  Check if entity is a valid string id
+      matches = rIdExp.exec( entity );
+
+      //  Get media element by id or object reference
+      this.media = matches && matches.length && matches[ 2 ] ?
+                     document.getElementById( matches[ 2 ] ) :
+                     entity;
+
+      //  Create an audio or video element property reference
+      this[ ( this.media.nodeName && this.media.nodeName.toLowerCase() ) || "video" ] = this.media;
+
+      //  Register new instance
+      Popcorn.instances.push( this );
+
+      this.options = options || {};
+
+      this.isDestroyed = false;
+
+      this.data = {
+
+        // Executed by either timeupdate event or in rAF loop
+        timeUpdate: Popcorn.nop,
+
+        // Allows disabling a plugin per instance
+        disabled: [],
+
+        // Stores DOM event queues by type
+        events: {},
+
+        // Stores Special event hooks data
+        hooks: {},
+
+        // Store track event history data
+        history: [],
+
+        // Stores ad-hoc state related data]
+        state: {
+          volume: this.media.volume
+        },
+
+        // Store track event object references by trackId
+        trackRefs: {},
+
+        // Playback track event queues
+        trackEvents: {
+          byStart: [{
+
+            start: -1,
+            end: -1
+          }],
+          byEnd: [{
+            start: -1,
+            end: -1
+          }],
+          animating: [],
+          startIndex: 0,
+          endIndex: 0,
+          previousUpdateTime: -1
+        }
+      };
+
+      //  Wrap true ready check
+      var isReady = function( that ) {
+
+        var duration, videoDurationPlus;
+
+        if ( that.media.readyState >= 2 ) {
+          //  Adding padding to the front and end of the arrays
+          //  this is so we do not fall off either end
+
+          duration = that.media.duration;
+          //  Check for no duration info (NaN)
+          videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
+
+          Popcorn.addTrackEvent( that, {
+            start: videoDurationPlus,
+            end: videoDurationPlus
+          });
+
+          if ( that.options.frameAnimation ) {
+            //  if Popcorn is created with frameAnimation option set to true,
+            //  requestAnimFrame is used instead of "timeupdate" media event.
+            //  This is for greater frame time accuracy, theoretically up to
+            //  60 frames per second as opposed to ~4 ( ~every 15-250ms)
+            that.data.timeUpdate = function () {
+
+              Popcorn.timeUpdate( that, {} );
+
+              that.trigger( "timeupdate" );
+
+              !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
+            };
+
+            !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
+
+          } else {
+
+            that.data.timeUpdate = function( event ) {
+              Popcorn.timeUpdate( that, event );
+            };
+
+            if ( !that.isDestroyed ) {
+              that.media.addEventListener( "timeupdate", that.data.timeUpdate, false );
+            }
+          }
+        } else {
+          global.setTimeout(function() {
+            isReady( that );
+          }, 1 );
+        }
+      };
+
+      isReady( this );
+
+      return this;
+    }
+  };
+
+  //  Extend constructor prototype to instance prototype
+  //  Allows chaining methods to instances
+  Popcorn.p.init.prototype = Popcorn.p;
+
+  Popcorn.forEach = function( obj, fn, context ) {
+
+    if ( !obj || !fn ) {
+      return {};
+    }
+
+    context = context || this;
+
+    var key, len;
+
+    // Use native whenever possible
+    if ( forEach && obj.forEach === forEach ) {
+      return obj.forEach( fn, context );
+    }
+
+    if ( toString.call( obj ) === "[object NodeList]" ) {
+      for ( key = 0, len = obj.length; key < len; key++ ) {
+        fn.call( context, obj[ key ], key, obj );
+      }
+      return obj;
+    }
+
+    for ( key in obj ) {
+      if ( hasOwn.call( obj, key ) ) {
+        fn.call( context, obj[ key ], key, obj );
+      }
+    }
+    return obj;
+  };
+
+  Popcorn.extend = function( obj ) {
+    var dest = obj, src = slice.call( arguments, 1 );
+
+    Popcorn.forEach( src, function( copy ) {
+      for ( var prop in copy ) {
+        dest[ prop ] = copy[ prop ];
+      }
+    });
+
+    return dest;
+  };
+
+
+  // A Few reusable utils, memoized onto Popcorn
+  Popcorn.extend( Popcorn, {
+    noConflict: function( deep ) {
+
+      if ( deep ) {
+        global.Popcorn = _Popcorn;
+      }
+
+      return Popcorn;
+    },
+    error: function( msg ) {
+      throw new Error( msg );
+    },
+    guid: function( prefix ) {
+      Popcorn.guid.counter++;
+      return  ( prefix ? prefix : "" ) + ( +new Date() + Popcorn.guid.counter );
+    },
+    sizeOf: function( obj ) {
+      var size = 0;
+
+      for ( var prop in obj ) {
+        size++;
+      }
+
+      return size;
+    },
+    isArray: Array.isArray || function( array ) {
+      return toString.call( array ) === "[object Array]";
+    },
+
+    nop: function() {},
+
+    position: function( elem ) {
+
+      var clientRect = elem.getBoundingClientRect(),
+          bounds = {},
+          doc = elem.ownerDocument,
+          docElem = document.documentElement,
+          body = document.body,
+          clientTop, clientLeft, scrollTop, scrollLeft, top, left;
+
+      //  Determine correct clientTop/Left
+      clientTop = docElem.clientTop || body.clientTop || 0;
+      clientLeft = docElem.clientLeft || body.clientLeft || 0;
+
+      //  Determine correct scrollTop/Left
+      scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop );
+      scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft );
+
+      //  Temp top/left
+      top = Math.ceil( clientRect.top + scrollTop - clientTop );
+      left = Math.ceil( clientRect.left + scrollLeft - clientLeft );
+
+      for ( var p in clientRect ) {
+        bounds[ p ] = Math.round( clientRect[ p ] );
+      }
+
+      return Popcorn.extend({}, bounds, { top: top, left: left });
+    },
+
+    disable: function( instance, plugin ) {
+
+      var disabled = instance.data.disabled;
+
+      if ( disabled.indexOf( plugin ) === -1 ) {
+        disabled.push( plugin );
+      }
+
+      refresh( instance );
+
+      return instance;
+    },
+    enable: function( instance, plugin ) {
+
+      var disabled = instance.data.disabled,
+          index = disabled.indexOf( plugin );
+
+      if ( index > -1 ) {
+        disabled.splice( index, 1 );
+      }
+
+      refresh( instance );
+
+      return instance;
+    },
+    destroy: function( instance ) {
+      var events = instance.data.events,
+          singleEvent, item, fn;
+
+      //  Iterate through all events and remove them
+      for ( item in events ) {
+        singleEvent = events[ item ];
+        for ( fn in singleEvent ) {
+          delete singleEvent[ fn ];
+        }
+        events[ item ] = null;
+      }
+
+      if ( !instance.isDestroyed ) {
+        instance.data.timeUpdate && instance.media.removeEventListener( "timeupdate", instance.data.timeUpdate, false );
+        instance.isDestroyed = true;
+      }
+    }
+  });
+
+  //  Memoized GUID Counter
+  Popcorn.guid.counter = 1;
+
+  //  Factory to implement getters, setters and controllers
+  //  as Popcorn instance methods. The IIFE will create and return
+  //  an object with defined methods
+  Popcorn.extend(Popcorn.p, (function() {
+
+      var methods = "load play pause currentTime playbackRate volume duration preload playbackRate " +
+                    "autoplay loop controls muted buffered readyState seeking paused played seekable ended",
+          ret = {};
+
+
+      //  Build methods, store in object that is returned and passed to extend
+      Popcorn.forEach( methods.split( /\s+/g ), function( name ) {
+
+        ret[ name ] = function( arg ) {
+
+          if ( typeof this.media[ name ] === "function" ) {
+
+            // Support for shorthanded play(n)/pause(n) jump to currentTime
+            // If arg is not null or undefined and called by one of the
+            // allowed shorthandable methods, then set the currentTime
+            // Supports time as seconds or SMPTE
+            if ( arg != null && /play|pause/.test( name ) ) {
+              this.media.currentTime = Popcorn.util.toSeconds( arg );
+            }
+
+            this.media[ name ]();
+
+            return this;
+          }
+
+
+          if ( arg != null ) {
+
+            this.media[ name ] = arg;
+
+            return this;
+          }
+
+          return this.media[ name ];
+        };
+      });
+
+      return ret;
+
+    })()
+  );
+
+  Popcorn.forEach( "enable disable".split(" "), function( method ) {
+    Popcorn.p[ method ] = function( plugin ) {
+      return Popcorn[ method ]( this, plugin );
+    };
+  });
+
+  Popcorn.extend(Popcorn.p, {
+
+    //  Rounded currentTime
+    roundTime: function() {
+      return -~this.media.currentTime;
+    },
+
+    //  Attach an event to a single point in time
+    exec: function( time, fn ) {
+
+      //  Creating a one second track event with an empty end
+      Popcorn.addTrackEvent( this, {
+        start: time,
+        end: time + 1,
+        _running: false,
+        _natives: {
+          start: fn || Popcorn.nop,
+          end: Popcorn.nop,
+          type: "exec"
+        }
+      });
+
+      return this;
+    },
+
+    // Mute the calling media, optionally toggle
+    mute: function( toggle ) {
+
+      var event = toggle == null || toggle === true ? "muted" : "unmuted";
+
+      // If `toggle` is explicitly `false`,
+      // unmute the media and restore the volume level
+      if ( event === "unmuted" ) {
+        this.media.muted = false;
+        this.media.volume = this.data.state.volume;
+      }
+
+      // If `toggle` is either null or undefined,
+      // save the current volume and mute the media element
+      if ( event === "muted" ) {
+        this.data.state.volume = this.media.volume;
+        this.media.muted = true;
+      }
+
+      // Trigger either muted|unmuted event
+      this.trigger( event );
+
+      return this;
+    },
+
+    // Convenience method, unmute the calling media
+    unmute: function( toggle ) {
+
+      return this.mute( toggle == null ? false : !toggle );
+    },
+
+    // Get the client bounding box of an instance element
+    position: function() {
+      return Popcorn.position( this.media );
+    },
+
+    // Toggle a plugin's playback behaviour (on or off) per instance
+    toggle: function( plugin ) {
+      return Popcorn[ this.data.disabled.indexOf( plugin ) > -1 ? "enable" : "disable" ]( this, plugin );
+    },
+
+    // Set default values for plugin options objects per instance
+    defaults: function( plugin, defaults ) {
+
+      // If an array of default configurations is provided,
+      // iterate and apply each to this instance
+      if ( Popcorn.isArray( plugin ) ) {
+
+        Popcorn.forEach( plugin, function( obj ) {
+          for ( var name in obj ) {
+            this.defaults( name, obj[ name ] );
+          }
+        }, this );
+
+        return this;
+      }
+
+      if ( !this.options.defaults ) {
+        this.options.defaults = {};
+      }
+
+      if ( !this.options.defaults[ plugin ] ) {
+        this.options.defaults[ plugin ] = {};
+      }
+
+      Popcorn.extend( this.options.defaults[ plugin ], defaults );
+
+      return this;
+    }
+  });
+
+  Popcorn.Events  = {
+    UIEvents: "blur focus focusin focusout load resize scroll unload",
+    MouseEvents: "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave click dblclick",
+    Events: "loadstart progress suspend emptied stalled play pause " +
+            "loadedmetadata loadeddata waiting playing canplay canplaythrough " +
+            "seeking seeked timeupdate ended ratechange durationchange volumechange"
+  };
+
+  Popcorn.Events.Natives = Popcorn.Events.UIEvents + " " +
+                           Popcorn.Events.MouseEvents + " " +
+                           Popcorn.Events.Events;
+
+  internal.events.apiTypes = [ "UIEvents", "MouseEvents", "Events" ];
+
+  // Privately compile events table at load time
+  (function( events, data ) {
+
+    var apis = internal.events.apiTypes,
+    eventsList = events.Natives.split( /\s+/g ),
+    idx = 0, len = eventsList.length, prop;
+
+    for( ; idx < len; idx++ ) {
+      data.hash[ eventsList[idx] ] = true;
+    }
+
+    apis.forEach(function( val, idx ) {
+
+      data.apis[ val ] = {};
+
+      var apiEvents = events[ val ].split( /\s+/g ),
+      len = apiEvents.length,
+      k = 0;
+
+      for ( ; k < len; k++ ) {
+        data.apis[ val ][ apiEvents[ k ] ] = true;
+      }
+    });
+  })( Popcorn.Events, internal.events );
+
+  Popcorn.events = {
+
+    isNative: function( type ) {
+      return !!internal.events.hash[ type ];
+    },
+    getInterface: function( type ) {
+
+      if ( !Popcorn.events.isNative( type ) ) {
+        return false;
+      }
+
+      var eventApi = internal.events,
+        apis = eventApi.apiTypes,
+        apihash = eventApi.apis,
+        idx = 0, len = apis.length, api, tmp;
+
+      for ( ; idx < len; idx++ ) {
+        tmp = apis[ idx ];
+
+        if ( apihash[ tmp ][ type ] ) {
+          api = tmp;
+          break;
+        }
+      }
+      return api;
+    },
+    //  Compile all native events to single array
+    all: Popcorn.Events.Natives.split( /\s+/g ),
+    //  Defines all Event handling static functions
+    fn: {
+      trigger: function( type, data ) {
+
+        var eventInterface, evt;
+        //  setup checks for custom event system
+        if ( this.data.events[ type ] && Popcorn.sizeOf( this.data.events[ type ] ) ) {
+
+          eventInterface  = Popcorn.events.getInterface( type );
+
+          if ( eventInterface ) {
+
+            evt = document.createEvent( eventInterface );
+            evt.initEvent( type, true, true, global, 1 );
+
+            this.media.dispatchEvent( evt );
+
+            return this;
+          }
+
+          //  Custom events
+          Popcorn.forEach( this.data.events[ type ], function( obj, key ) {
+
+            obj.call( this, data );
+
+          }, this );
+
+        }
+
+        return this;
+      },
+      listen: function( type, fn ) {
+
+        var self = this,
+            hasEvents = true,
+            eventHook = Popcorn.events.hooks[ type ],
+            origType = type,
+            tmp;
+
+        if ( !this.data.events[ type ] ) {
+          this.data.events[ type ] = {};
+          hasEvents = false;
+        }
+
+        // Check and setup event hooks
+        if ( eventHook ) {
+
+          // Execute hook add method if defined
+          if ( eventHook.add ) {
+            eventHook.add.call( this, {}, fn );
+          }
+
+          // Reassign event type to our piggyback event type if defined
+          if ( eventHook.bind ) {
+            type = eventHook.bind;
+          }
+
+          // Reassign handler if defined
+          if ( eventHook.handler ) {
+            tmp = fn;
+
+            fn = function wrapper( event ) {
+              eventHook.handler.call( self, event, tmp );
+            };
+          }
+
+          // assume the piggy back event is registered
+          hasEvents = true;
+
+          // Setup event registry entry
+          if ( !this.data.events[ type ] ) {
+            this.data.events[ type ] = {};
+            // Toggle if the previous assumption was untrue
+            hasEvents = false;
+          }
+        }
+
+        //  Register event and handler
+        this.data.events[ type ][ fn.name || ( fn.toString() + Popcorn.guid() ) ] = fn;
+
+        // only attach one event of any type
+        if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) {
+
+          this.media.addEventListener( type, function( event ) {
+
+            Popcorn.forEach( self.data.events[ type ], function( obj, key ) {
+              if ( typeof obj === "function" ) {
+                obj.call( self, event );
+              }
+            });
+
+          }, false);
+        }
+        return this;
+      },
+      unlisten: function( type, fn ) {
+
+        if ( this.data.events[ type ] && this.data.events[ type ][ fn ] ) {
+
+          delete this.data.events[ type ][ fn ];
+
+          return this;
+        }
+
+        this.data.events[ type ] = null;
+
+        return this;
+      }
+    },
+    hooks: {
+      canplayall: {
+        bind: "canplaythrough",
+        add: function( event, callback ) {
+
+          var state = false;
+
+          if ( this.media.readyState ) {
+
+            callback.call( this, event );
+
+            state = true;
+          }
+
+          this.data.hooks.canplayall = {
+            fired: state
+          };
+        },
+        // declare special handling instructions
+        handler: function canplayall( event, callback ) {
+
+          if ( !this.data.hooks.canplayall.fired ) {
+            // trigger original user callback once
+            callback.call( this, event );
+
+            this.data.hooks.canplayall.fired = true;
+          }
+        }
+      }
+    }
+  };
+
+  //  Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances
+  Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) {
+    Popcorn.p[ key ] = Popcorn.events.fn[ key ];
+  });
+
+  // Internal Only - Adds track events to the instance object
+  Popcorn.addTrackEvent = function( obj, track ) {
+
+    // Determine if this track has default options set for it
+    // If so, apply them to the track object
+    if ( track && track._natives && track._natives.type &&
+        ( obj.options.defaults && obj.options.defaults[ track._natives.type ] ) ) {
+
+      track = Popcorn.extend( {}, obj.options.defaults[ track._natives.type ], track );
+    }
+
+    if ( track._natives ) {
+      //  Supports user defined track event id
+      track._id = !track.id ? Popcorn.guid( track._natives.type ) : track.id;
+
+      //  Push track event ids into the history
+      obj.data.history.push( track._id );
+    }
+
+    track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate );
+    track.end   = Popcorn.util.toSeconds( track.end, obj.options.framerate );
+
+    //  Store this definition in an array sorted by times
+    var byStart = obj.data.trackEvents.byStart,
+        byEnd = obj.data.trackEvents.byEnd,
+        startIndex, endIndex,
+        currentTime;
+
+    for ( startIndex = byStart.length - 1; startIndex >= 0; startIndex-- ) {
+
+      if ( track.start >= byStart[ startIndex ].start ) {
+        byStart.splice( startIndex + 1, 0, track );
+        break;
+      }
+    }
+
+    for ( endIndex = byEnd.length - 1; endIndex >= 0; endIndex-- ) {
+
+      if ( track.end > byEnd[ endIndex ].end ) {
+        byEnd.splice( endIndex + 1, 0, track );
+        break;
+      }
+    }
+
+    // Display track event immediately if it's enabled and current
+    if ( track._natives &&
+        ( !!Popcorn.registryByName[ track._natives.type ] || !!obj[ track._natives.type ] ) ) {
+
+      currentTime = obj.media.currentTime;
+      if ( track.end > currentTime &&
+        track.start <= currentTime &&
+        obj.data.disabled.indexOf( track._natives.type ) === -1 ) {
+
+        track._running = true;
+        track._natives.start.call( obj, null, track );
+
+        if ( obj.options.frameAnimation &&
+          track._natives.frame ) {
+
+          obj.data.trackEvents.animating.push( track );
+          track._natives.frame.call( obj, null, track, currentTime );
+        }
+      }
+    }
+
+    // update startIndex and endIndex
+    if ( startIndex <= obj.data.trackEvents.startIndex &&
+      track.start <= obj.data.trackEvents.previousUpdateTime ) {
+
+      obj.data.trackEvents.startIndex++;
+    }
+
+    if ( endIndex <= obj.data.trackEvents.endIndex &&
+      track.end < obj.data.trackEvents.previousUpdateTime ) {
+
+      obj.data.trackEvents.endIndex++;
+    }
+
+    this.timeUpdate( obj, null, true );
+
+    // Store references to user added trackevents in ref table
+    if ( track._id ) {
+      Popcorn.addTrackEvent.ref( obj, track );
+    }
+  };
+
+  // Internal Only - Adds track event references to the instance object's trackRefs hash table
+  Popcorn.addTrackEvent.ref = function( obj, track ) {
+    obj.data.trackRefs[ track._id ] = track;
+
+    return obj;
+  };
+
+  Popcorn.removeTrackEvent  = function( obj, trackId ) {
+
+    var historyLen = obj.data.history.length,
+        indexWasAt = 0,
+        byStart = [],
+        byEnd = [],
+        animating = [],
+        history = [];
+
+    Popcorn.forEach( obj.data.trackEvents.byStart, function( o, i, context ) {
+      // Preserve the original start/end trackEvents
+      if ( !o._id ) {
+        byStart.push( obj.data.trackEvents.byStart[i] );
+        byEnd.push( obj.data.trackEvents.byEnd[i] );
+      }
+
+      // Filter for user track events (vs system track events)
+      if ( o._id ) {
+
+        // Filter for the trackevent to remove
+        if ( o._id !== trackId ) {
+          byStart.push( obj.data.trackEvents.byStart[i] );
+          byEnd.push( obj.data.trackEvents.byEnd[i] );
+        }
+
+        //  Capture the position of the track being removed.
+        if ( o._id === trackId ) {
+          indexWasAt = i;
+          o._natives._teardown && o._natives._teardown.call( obj, o );
+        }
+      }
+
+    });
+
+    if ( obj.data.trackEvents.animating.length ) {
+      Popcorn.forEach( obj.data.trackEvents.animating, function( o, i, context ) {
+        // Preserve the original start/end trackEvents
+        if ( !o._id ) {
+          animating.push( obj.data.trackEvents.animating[i] );
+        }
+
+        // Filter for user track events (vs system track events)
+        if ( o._id ) {
+          // Filter for the trackevent to remove
+          if ( o._id !== trackId ) {
+            animating.push( obj.data.trackEvents.animating[i] );
+          }
+        }
+      });
+    }
+
+    //  Update
+    if ( indexWasAt <= obj.data.trackEvents.startIndex ) {
+      obj.data.trackEvents.startIndex--;
+    }
+
+    if ( indexWasAt <= obj.data.trackEvents.endIndex ) {
+      obj.data.trackEvents.endIndex--;
+    }
+
+    obj.data.trackEvents.byStart = byStart;
+    obj.data.trackEvents.byEnd = byEnd;
+    obj.data.trackEvents.animating = animating;
+
+    for ( var i = 0; i < historyLen; i++ ) {
+      if ( obj.data.history[ i ] !== trackId ) {
+        history.push( obj.data.history[ i ] );
+      }
+    }
+
+    // Update ordered history array
+    obj.data.history = history;
+
+    // Update track event references
+    Popcorn.removeTrackEvent.ref( obj, trackId );
+  };
+
+  // Internal Only - Removes track event references from instance object's trackRefs hash table
+  Popcorn.removeTrackEvent.ref = function( obj, trackId ) {
+    delete obj.data.trackRefs[ trackId ];
+
+    return obj;
+  };
+
+  // Return an array of track events bound to this instance object
+  Popcorn.getTrackEvents = function( obj ) {
+
+    var trackevents = [],
+      refs = obj.data.trackEvents.byStart,
+      length = refs.length,
+      idx = 0,
+      ref;
+
+    for ( ; idx < length; idx++ ) {
+      ref = refs[ idx ];
+      // Return only user attributed track event references
+      if ( ref._id ) {
+        trackevents.push( ref );
+      }
+    }
+
+    return trackevents;
+  };
+
+  // Internal Only - Returns an instance object's trackRefs hash table
+  Popcorn.getTrackEvents.ref = function( obj ) {
+    return obj.data.trackRefs;
+  };
+
+  // Return a single track event bound to this instance object
+  Popcorn.getTrackEvent = function( obj, trackId ) {
+    return obj.data.trackRefs[ trackId ];
+  };
+
+  // Internal Only - Returns an instance object's track reference by track id
+  Popcorn.getTrackEvent.ref = function( obj, trackId ) {
+    return obj.data.trackRefs[ trackId ];
+  };
+
+  Popcorn.getLastTrackEventId = function( obj ) {
+    return obj.data.history[ obj.data.history.length - 1 ];
+  };
+
+  Popcorn.timeUpdate = function( obj, event ) {
+
+    var currentTime = obj.media.currentTime,
+        previousTime = obj.data.trackEvents.previousUpdateTime,
+        tracks = obj.data.trackEvents,
+        animating = tracks.animating,
+        end = tracks.endIndex,
+        start = tracks.startIndex,
+        animIndex = 0,
+
+        registryByName = Popcorn.registryByName,
+
+        byEnd, byStart, byAnimate, natives, type;
+
+    //  Playbar advancing
+    if ( previousTime <= currentTime ) {
+
+      while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end <= currentTime ) {
+
+        byEnd = tracks.byEnd[ end ];
+        natives = byEnd._natives;
+        type = natives && natives.type;
+
+        //  If plugin does not exist on this instance, remove it
+        if ( !natives ||
+            ( !!registryByName[ type ] ||
+              !!obj[ type ] ) ) {
+
+          if ( byEnd._running === true ) {
+            byEnd._running = false;
+            natives.end.call( obj, event, byEnd );
+          }
+
+          end++;
+        } else {
+          // remove track event
+          Popcorn.removeTrackEvent( obj, byEnd._id );
+          return;
+        }
+      }
+
+      while ( tracks.byStart[ start ] && tracks.byStart[ start ].start <= currentTime ) {
+
+        byStart = tracks.byStart[ start ];
+        natives = byStart._natives;
+        type = natives && natives.type;
+
+        //  If plugin does not exist on this instance, remove it
+        if ( !natives ||
+            ( !!registryByName[ type ] ||
+              !!obj[ type ] ) ) {
+
+          if ( byStart.end > currentTime &&
+                byStart._running === false &&
+                  obj.data.disabled.indexOf( type ) === -1 ) {
+
+            byStart._running = true;
+            natives.start.call( obj, event, byStart );
+
+            // If the `frameAnimation` option is used,
+            // push the current byStart object into the `animating` cue
+            if ( obj.options.frameAnimation &&
+                ( byStart && byStart._running && byStart._natives.frame ) ) {
+
+              animating.push( byStart );
+            }
+          }
+          start++;
+        } else {
+          // remove track event
+          Popcorn.removeTrackEvent( obj, byStart._id );
+          return;
+        }
+      }
+
+      // If the `frameAnimation` option is used, iterate the animating track
+      // and execute the `frame` callback
+      if ( obj.options.frameAnimation ) {
+        while ( animIndex < animating.length ) {
+
+          byAnimate = animating[ animIndex ];
+
+          if ( !byAnimate._running ) {
+            animating.splice( animIndex, 1 );
+          } else {
+            byAnimate._natives.frame.call( obj, event, byAnimate, currentTime );
+            animIndex++;
+          }
+        }
+      }
+
+    // Playbar receding
+    } else if ( previousTime > currentTime ) {
+
+      while ( tracks.byStart[ start ] && tracks.byStart[ start ].start > currentTime ) {
+
+        byStart = tracks.byStart[ start ];
+        natives = byStart._natives;
+        type = natives && natives.type;
+
+        // if plugin does not exist on this instance, remove it
+        if ( !natives ||
+            ( !!registryByName[ type ] ||
+              !!obj[ type ] ) ) {
+
+          if ( byStart._running === true ) {
+            byStart._running = false;
+            natives.end.call( obj, event, byStart );
+          }
+          start--;
+        } else {
+          // remove track event
+          Popcorn.removeTrackEvent( obj, byStart._id );
+          return;
+        }
+      }
+
+      while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end > currentTime ) {
+
+        byEnd = tracks.byEnd[ end ];
+        natives = byEnd._natives;
+        type = natives && natives.type;
+
+        // if plugin does not exist on this instance, remove it
+        if ( !natives ||
+            ( !!registryByName[ type ] ||
+              !!obj[ type ] ) ) {
+
+          if ( byEnd.start <= currentTime &&
+                byEnd._running === false  &&
+                  obj.data.disabled.indexOf( type ) === -1 ) {
+
+            byEnd._running = true;
+            natives.start.call( obj, event, byEnd );
+
+            // If the `frameAnimation` option is used,
+            // push the current byEnd object into the `animating` cue
+            if ( obj.options.frameAnimation &&
+                  ( byEnd && byEnd._running && byEnd._natives.frame ) ) {
+
+              animating.push( byEnd );
+            }
+          }
+          end--;
+        } else {
+          // remove track event
+          Popcorn.removeTrackEvent( obj, byEnd._id );
+          return;
+        }
+      }
+
+      // If the `frameAnimation` option is used, iterate the animating track
+      // and execute the `frame` callback
+      if ( obj.options.frameAnimation ) {
+        while ( animIndex < animating.length ) {
+
+          byAnimate = animating[ animIndex ];
+
+          if ( !byAnimate._running ) {
+            animating.splice( animIndex, 1 );
+          } else {
+            byAnimate._natives.frame.call( obj, event, byAnimate, currentTime );
+            animIndex++;
+          }
+        }
+      }
+    // time bar is not moving ( video is paused )
+    }
+
+    tracks.endIndex = end;
+    tracks.startIndex = start;
+    tracks.previousUpdateTime = currentTime;
+  };
+
+  //  Map and Extend TrackEvent functions to all Popcorn instances
+  Popcorn.extend( Popcorn.p, {
+
+    getTrackEvents: function() {
+      return Popcorn.getTrackEvents.call( null, this );
+    },
+
+    getTrackEvent: function( id ) {
+      return Popcorn.getTrackEvent.call( null, this, id );
+    },
+
+    getLastTrackEventId: function() {
+      return Popcorn.getLastTrackEventId.call( null, this );
+    },
+
+    removeTrackEvent: function( id ) {
+
+      Popcorn.removeTrackEvent.call( null, this, id );
+      return this;
+    },
+
+    removePlugin: function( name ) {
+      Popcorn.removePlugin.call( null, this, name );
+      return this;
+    },
+
+    timeUpdate: function( event ) {
+      Popcorn.timeUpdate.call( null, this, event );
+      return this;
+    },
+
+    destroy: function() {
+      Popcorn.destroy.call( null, this );
+      return this;
+    }
+  });
+
+  //  Plugin manifests
+  Popcorn.manifest = {};
+  //  Plugins are registered
+  Popcorn.registry = [];
+  Popcorn.registryByName = {};
+  //  An interface for extending Popcorn
+  //  with plugin functionality
+  Popcorn.plugin = function( name, definition, manifest ) {
+
+    if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
+      Popcorn.error( "'" + name + "' is a protected function name" );
+      return;
+    }
+
+    //  Provides some sugar, but ultimately extends
+    //  the definition into Popcorn.p
+    var reserved = [ "start", "end" ],
+        plugin = {},
+        setup,
+        isfn = typeof definition === "function",
+        methods = [ "_setup", "_teardown", "start", "end", "frame" ];
+
+    // combines calls of two function calls into one
+    var combineFn = function( first, second ) {
+
+      first = first || Popcorn.nop;
+      second = second || Popcorn.nop;
+
+      return function() {
+        first.apply( this, arguments );
+        second.apply( this, arguments );
+      };
+    };
+
+    //  If `manifest` arg is undefined, check for manifest within the `definition` object
+    //  If no `definition.manifest`, an empty object is a sufficient fallback
+    Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
+
+    // apply safe, and empty default functions
+    methods.forEach(function( method ) {
+      definition[ method ] = safeTry( definition[ method ] || Popcorn.nop, name );
+    });
+
+    var pluginFn = function( setup, options ) {
+
+      if ( !options ) {
+        return this;
+      }
+
+      //  Storing the plugin natives
+      var natives = options._natives = {},
+          compose = "",
+          defaults, originalOpts, manifestOpts, mergedSetupOpts;
+
+      Popcorn.extend( natives, setup );
+
+      options._natives.type = name;
+      options._running = false;
+
+      natives.start = natives.start || natives[ "in" ];
+      natives.end = natives.end || natives[ "out" ];
+
+      // extend teardown to always call end if running
+      natives._teardown = combineFn(function() {
+
+        var args = slice.call( arguments );
+
+        // end function signature is not the same as teardown,
+        // put null on the front of arguments for the event parameter
+        args.unshift( null );
+
+        // only call end if event is running
+        args[ 1 ]._running && natives.end.apply( this, args );
+      }, natives._teardown );
+
+      // Check for previously set default options
+      defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ];
+
+      // default to an empty string if no effect exists
+      // split string into an array of effects
+      options.compose = options.compose && options.compose.split( " " ) || [];
+      options.effect = options.effect && options.effect.split( " " ) || [];
+
+      // join the two arrays together
+      options.compose = options.compose.concat( options.effect );
+
+      options.compose.forEach(function( composeOption ) {
+
+        // if the requested compose is garbage, throw it away
+        compose = Popcorn.compositions[ composeOption ] || {};
+
+        // extends previous functions with compose function
+        methods.forEach(function( method ) {
+          natives[ method ] = combineFn( natives[ method ], compose[ method ] );
+        });
+      });
+
+      //  Ensure a manifest object, an empty object is a sufficient fallback
+      options._natives.manifest = manifest;
+
+      //  Checks for expected properties
+      if ( !( "start" in options ) ) {
+        options.start = options[ "in" ] || 0;
+      }
+
+      if ( !( "end" in options ) ) {
+        options.end = options[ "out" ] || this.duration() || Number.MAX_VALUE;
+      }
+
+      // Merge with defaults if they exist, make sure per call is prioritized
+      mergedSetupOpts = defaults ? Popcorn.extend( {}, defaults, options ) :
+                          options;
+
+      // Resolves 239, 241, 242
+      if ( !mergedSetupOpts.target ) {
+
+        //  Sometimes the manifest may be missing entirely
+        //  or it has an options object that doesn't have a `target` property
+        manifestOpts = "options" in manifest && manifest.options;
+
+        mergedSetupOpts.target = manifestOpts && "target" in manifestOpts && manifestOpts.target;
+      }
+
+      // Trigger _setup method if exists
+      options._natives._setup && options._natives._setup.call( this, mergedSetupOpts );
+
+      // Create new track event for this instance
+      Popcorn.addTrackEvent( this, Popcorn.extend( mergedSetupOpts, options ) );
+
+      //  Future support for plugin event definitions
+      //  for all of the native events
+      Popcorn.forEach( setup, function( callback, type ) {
+
+        if ( type !== "type" ) {
+
+          if ( reserved.indexOf( type ) === -1 ) {
+
+            this.listen( type, callback );
+          }
+        }
+
+      }, this );
+
+      return this;
+    };
+
+    //  Assign new named definition
+    plugin[ name ] = function( options ) {
+      return pluginFn.call( this, isfn ? definition.call( this, options ) : definition,
+                                  options );
+    };
+
+    //  Extend Popcorn.p with new named definition
+    Popcorn.extend( Popcorn.p, plugin );
+
+    //  Push into the registry
+    var entry = {
+      fn: plugin[ name ],
+      definition: definition,
+      base: definition,
+      parents: [],
+      name: name
+    };
+    Popcorn.registry.push(
+       Popcorn.extend( plugin, entry, {
+        type: name
+      })
+    );
+    Popcorn.registryByName[ name ] = entry;
+
+    return plugin;
+  };
+
+  // Storage for plugin function errors
+  Popcorn.plugin.errors = [];
+
+  // Returns wrapped plugin function
+  function safeTry( fn, pluginName ) {
+    return function() {
+
+      //  When Popcorn.plugin.debug is true, do not suppress errors
+      if ( Popcorn.plugin.debug ) {
+        return fn.apply( this, arguments );
+      }
+
+      try {
+        return fn.apply( this, arguments );
+      } catch ( ex ) {
+
+        // Push plugin function errors into logging queue
+        Popcorn.plugin.errors.push({
+          plugin: pluginName,
+          thrown: ex,
+          source: fn.toString()
+        });
+
+        // Trigger an error that the instance can listen for
+        // and react to
+        this.trigger( "error", Popcorn.plugin.errors );
+      }
+    };
+  }
+
+  // Debug-mode flag for plugin development
+  Popcorn.plugin.debug = false;
+
+  //  removePlugin( type ) removes all tracks of that from all instances of popcorn
+  //  removePlugin( obj, type ) removes all tracks of type from obj, where obj is a single instance of popcorn
+  Popcorn.removePlugin = function( obj, name ) {
+
+    //  Check if we are removing plugin from an instance or from all of Popcorn
+    if ( !name ) {
+
+      //  Fix the order
+      name = obj;
+      obj = Popcorn.p;
+
+      if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
+        Popcorn.error( "'" + name + "' is a protected function name" );
+        return;
+      }
+
+      var registryLen = Popcorn.registry.length,
+          registryIdx;
+
+      // remove plugin reference from registry
+      for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
+        if ( Popcorn.registry[ registryIdx ].name === name ) {
+          Popcorn.registry.splice( registryIdx, 1 );
+          delete Popcorn.registryByName[ name ];
+          delete Popcorn.manifest[ name ];
+
+          // delete the plugin
+          delete obj[ name ];
+
+          // plugin found and removed, stop checking, we are done
+          return;
+        }
+      }
+
+    }
+
+    var byStart = obj.data.trackEvents.byStart,
+        byEnd = obj.data.trackEvents.byEnd,
+        animating = obj.data.trackEvents.animating,
+        idx, sl;
+
+    // remove all trackEvents
+    for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) {
+
+      if ( ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) &&
+                ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) ) {
+
+        byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] );
+
+        byStart.splice( idx, 1 );
+        byEnd.splice( idx, 1 );
+
+        // update for loop if something removed, but keep checking
+        idx--; sl--;
+        if ( obj.data.trackEvents.startIndex <= idx ) {
+          obj.data.trackEvents.startIndex--;
+          obj.data.trackEvents.endIndex--;
+        }
+      }
+    }
+
+    //remove all animating events
+    for ( idx = 0, sl = animating.length; idx < sl; idx++ ) {
+
+      if ( animating[ idx ] && animating[ idx ]._natives && animating[ idx ]._natives.type === name ) {
+
+        animating.splice( idx, 1 );
+
+        // update for loop if something removed, but keep checking
+        idx--; sl--;
+      }
+    }
+
+  };
+
+  Popcorn.compositions = {};
+
+  //  Plugin inheritance
+  Popcorn.compose = function( name, definition, manifest ) {
+
+    //  If `manifest` arg is undefined, check for manifest within the `definition` object
+    //  If no `definition.manifest`, an empty object is a sufficient fallback
+    Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
+
+    // register the effect by name
+    Popcorn.compositions[ name ] = definition;
+  };
+
+  Popcorn.plugin.effect = Popcorn.effect = Popcorn.compose;
+
+  // stores parsers keyed on filetype
+  Popcorn.parsers = {};
+
+  // An interface for extending Popcorn
+  // with parser functionality
+  Popcorn.parser = function( name, type, definition ) {
+
+    if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
+      Popcorn.error( "'" + name + "' is a protected function name" );
+      return;
+    }
+
+    // fixes parameters for overloaded function call
+    if ( typeof type === "function" && !definition ) {
+      definition = type;
+      type = "";
+    }
+
+    if ( typeof definition !== "function" || typeof type !== "string" ) {
+      return;
+    }
+
+    // Provides some sugar, but ultimately extends
+    // the definition into Popcorn.p
+
+    var natives = Popcorn.events.all,
+        parseFn,
+        parser = {};
+
+    parseFn = function( filename, callback ) {
+
+      if ( !filename ) {
+        return this;
+      }
+
+      var that = this;
+
+      Popcorn.xhr({
+        url: filename,
+        dataType: type,
+        success: function( data ) {
+
+          var tracksObject = definition( data ),
+              tracksData,
+              tracksDataLen,
+              tracksDef,
+              idx = 0;
+
+          tracksData = tracksObject.data || [];
+          tracksDataLen = tracksData.length;
+          tracksDef = null;
+
+          //  If no tracks to process, return immediately
+          if ( !tracksDataLen ) {
+            return;
+          }
+
+          //  Create tracks out of parsed object
+          for ( ; idx < tracksDataLen; idx++ ) {
+
+            tracksDef = tracksData[ idx ];
+
+            for ( var key in tracksDef ) {
+
+              if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) {
+
+                that[ key ]( tracksDef[ key ] );
+              }
+            }
+          }
+          if ( callback ) {
+            callback();
+          }
+        }
+      });
+
+      return this;
+    };
+
+    // Assign new named definition
+    parser[ name ] = parseFn;
+
+    // Extend Popcorn.p with new named definition
+    Popcorn.extend( Popcorn.p, parser );
+
+    // keys the function name by filetype extension
+    //Popcorn.parsers[ name ] = true;
+
+    return parser;
+  };
+
+  Popcorn.player = function( name, player ) {
+
+    player = player || {};
+
+    var playerFn = function( target, src, options ) {
+
+      options = options || {};
+
+      // List of events
+      var date = new Date() / 1000,
+          baselineTime = date,
+          currentTime = 0,
+          volume = 1,
+          muted = false,
+          events = {},
+
+          // The container div of the resource
+          container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) ||
+                        document.getElementById( target ) ||
+                          target,
+          basePlayer = {},
+          timeout,
+          popcorn;
+
+      // copies a div into the media object
+      for( var val in container ) {
+
+        if ( typeof container[ val ] === "object" ) {
+
+          basePlayer[ val ] = container[ val ];
+        } else if ( typeof container[ val ] === "function" ) {
+
+          basePlayer[ val ] = (function( value ) {
+
+            // this is a stupid ugly kludgy hack in honour of Safari
+            // in Safari a NodeList is a function, not an object
+            if ( "length" in container[ value ] && !container[ value ].call ) {
+
+              return container[ value ];
+            } else {
+
+              return function() {
+
+                return container[ value ].apply( container, arguments );
+              };
+            }
+          }( val ));
+        } else {
+
+          Popcorn.player.defineProperty( basePlayer, val, {
+            get: (function( value ) {
+
+              return function() {
+
+                return container[ value ];
+              };
+            }( val )),
+            set: Popcorn.nop,
+            configurable: true
+          });
+        }
+      }
+
+      var timeupdate = function() {
+
+        date = new Date() / 1000;
+
+        if ( !basePlayer.paused ) {
+
+          basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
+          basePlayer.dispatchEvent( "timeupdate" );
+          timeout = setTimeout( timeupdate, 10 );
+        }
+
+        baselineTime = date;
+      };
+
+      basePlayer.play = function() {
+
+        this.paused = false;
+
+        if ( basePlayer.readyState >= 4 ) {
+
+          baselineTime = new Date() / 1000;
+          basePlayer.dispatchEvent( "play" );
+          timeupdate();
+        }
+      };
+
+      basePlayer.pause = function() {
+
+        this.paused = true;
+        basePlayer.dispatchEvent( "pause" );
+      };
+
+      Popcorn.player.defineProperty( basePlayer, "currentTime", {
+        get: function() {
+
+          return currentTime;
+        },
+        set: function( val ) {
+
+          // make sure val is a number
+          currentTime = +val;
+          basePlayer.dispatchEvent( "timeupdate" );
+          return currentTime;
+        },
+        configurable: true
+      });
+
+      Popcorn.player.defineProperty( basePlayer, "volume", {
+        get: function() {
+
+          return volume;
+        },
+        set: function( val ) {
+
+          // make sure val is a number
+          volume = +val;
+          basePlayer.dispatchEvent( "volumechange" );
+          return volume;
+        },
+        configurable: true
+      });
+
+      Popcorn.player.defineProperty( basePlayer, "muted", {
+        get: function() {
+
+          return muted;
+        },
+        set: function( val ) {
+
+          // make sure val is a number
+          muted = +val;
+          basePlayer.dispatchEvent( "volumechange" );
+          return muted;
+        },
+        configurable: true
+      });
+
+      // Adds an event listener to the object
+      basePlayer.addEventListener = function( evtName, fn ) {
+
+        if ( !events[ evtName ] ) {
+
+          events[ evtName ] = [];
+        }
+
+        events[ evtName ].push( fn );
+        return fn;
+      };
+
+      // Can take event object or simple string
+      basePlayer.dispatchEvent = function( oEvent ) {
+
+        var evt,
+            self = this,
+            eventInterface,
+            eventName = oEvent.type;
+
+        // A string was passed, create event object
+        if ( !eventName ) {
+
+          eventName = oEvent;
+          eventInterface  = Popcorn.events.getInterface( eventName );
+
+          if ( eventInterface ) {
+
+            evt = document.createEvent( eventInterface );
+            evt.initEvent( eventName, true, true, window, 1 );
+          }
+        }
+
+        Popcorn.forEach( events[ eventName ], function( val ) {
+
+          val.call( self, evt, self );
+        });
+      };
+
+      // Attempt to get src from playerFn parameter
+      basePlayer.src = src || "";
+      basePlayer.readyState = 0;
+      basePlayer.duration = 0;
+      basePlayer.paused = true;
+      basePlayer.ended = 0;
+
+      if ( player._setup ) {
+
+        player._setup.call( basePlayer, options );
+      } else {
+
+        // there is no setup, which means there is nothing to load
+        basePlayer.readyState = 4;
+        basePlayer.dispatchEvent( "load" );
+        basePlayer.dispatchEvent( "loadeddata" );
+      }
+
+      // when a custom player is loaded, load basePlayer state into custom player
+      basePlayer.addEventListener( "load", function() {
+
+        // if a player is not ready before currentTime is called, this will set it after it is ready
+        basePlayer.currentTime = currentTime;
+
+        // same as above with volume and muted
+        basePlayer.volume = volume;
+        basePlayer.muted = muted;
+      });
+
+      basePlayer.addEventListener( "loadeddata", function() {
+
+        // if play was called before player ready, start playing video
+        !basePlayer.paused && basePlayer.play();
+      });
+
+      popcorn = new Popcorn.p.init( basePlayer, options );
+
+      return popcorn;
+    };
+
+    Popcorn[ name ] = Popcorn[ name ] || playerFn;
+  };
+
+  Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
+
+    object.__defineGetter__( description, options.get || Popcorn.nop );
+    object.__defineSetter__( description, options.set || Popcorn.nop );
+  };
+
+  //  Cache references to reused RegExps
+  var rparams = /\?/,
+  //  XHR Setup object
+  setup = {
+    url: "",
+    data: "",
+    dataType: "",
+    success: Popcorn.nop,
+    type: "GET",
+    async: true,
+    xhr: function() {
+      return new global.XMLHttpRequest();
+    }
+  };
+
+  Popcorn.xhr = function( options ) {
+
+    options.dataType = options.dataType && options.dataType.toLowerCase() || null;
+
+    if ( options.dataType &&
+         ( options.dataType === "jsonp" || options.dataType === "script" ) ) {
+
+      Popcorn.xhr.getJSONP(
+        options.url,
+        options.success,
+        options.dataType === "script"
+      );
+      return;
+    }
+
+    var settings = Popcorn.extend( {}, setup, options );
+
+    //  Create new XMLHttpRequest object
+    settings.ajax  = settings.xhr();
+
+    if ( settings.ajax ) {
+
+      if ( settings.type === "GET" && settings.data ) {
+
+        //  append query string
+        settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
+
+        //  Garbage collect and reset settings.data
+        settings.data = null;
+      }
+
+
+      settings.ajax.open( settings.type, settings.url, settings.async );
+      settings.ajax.send( settings.data || null );
+
+      return Popcorn.xhr.httpData( settings );
+    }
+  };
+
+
+  Popcorn.xhr.httpData = function( settings ) {
+
+    var data, json = null,
+        parser, xml = null;
+
+    settings.ajax.onreadystatechange = function() {
+
+      if ( settings.ajax.readyState === 4 ) {
+
+        try {
+          json = JSON.parse( settings.ajax.responseText );
+        } catch( e ) {
+          //suppress
+        }
+
+        data = {
+          xml: settings.ajax.responseXML,
+          text: settings.ajax.responseText,
+          json: json
+        };
+
+        // Normalize: data.xml is non-null in IE9 regardless of if response is valid xml
+        if ( !data.xml || !data.xml.documentElement ) {
+          data.xml = null;
+
+          try {
+            parser = new DOMParser();
+            xml = parser.parseFromString( settings.ajax.responseText, "text/xml" );
+
+            if ( !xml.getElementsByTagName( "parsererror" ).length ) {
+              data.xml = xml;
+            }
+          } catch ( e ) {
+            // data.xml remains null
+          }
+        }
+
+        //  If a dataType was specified, return that type of data
+        if ( settings.dataType ) {
+          data = data[ settings.dataType ];
+        }
+
+
+        settings.success.call( settings.ajax, data );
+
+      }
+    };
+    return data;
+  };
+
+  Popcorn.xhr.getJSONP = function( url, success, isScript ) {
+
+    var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
+      script = document.createElement( "script" ),
+      paramStr = url.split( "?" )[ 1 ],
+      isFired = false,
+      params = [],
+      callback, parts, callparam;
+
+    if ( paramStr && !isScript ) {
+      params = paramStr.split( "&" );
+    }
+
+    if ( params.length ) {
+      parts = params[ params.length - 1 ].split( "=" );
+    }
+
+    callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ]  ) : "jsonp";
+
+    if ( !paramStr && !isScript ) {
+      url += "?callback=" + callback;
+    }
+
+    if ( callback && !isScript ) {
+
+      //  If a callback name already exists
+      if ( !!window[ callback ] ) {
+        //  Create a new unique callback name
+        callback = Popcorn.guid( callback );
+      }
+
+      //  Define the JSONP success callback globally
+      window[ callback ] = function( data ) {
+        // Fire success callbacks
+        success && success( data );
+        isFired = true;
+      };
+
+      //  Replace callback param and callback name
+      url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
+    }
+
+    script.onload = function() {
+
+      //  Handling remote script loading callbacks
+      if ( isScript ) {
+        //  getScript
+        success && success();
+      }
+
+      //  Executing for JSONP requests
+      if ( isFired ) {
+        //  Garbage collect the callback
+        delete window[ callback ];
+      }
+      //  Garbage collect the script resource
+      head.removeChild( script );
+    };
+
+    script.src = url;
+
+    head.insertBefore( script, head.firstChild );
+
+    return;
+  };
+
+  Popcorn.getJSONP = Popcorn.xhr.getJSONP;
+
+  Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
+
+    return Popcorn.xhr.getJSONP( url, success, true );
+  };
+
+  Popcorn.util = {
+    // Simple function to parse a timestamp into seconds
+    // Acceptable formats are:
+    // HH:MM:SS.MMM
+    // HH:MM:SS;FF
+    // Hours and minutes are optional. They default to 0
+    toSeconds: function( timeStr, framerate ) {
+      // Hours and minutes are optional
+      // Seconds must be specified
+      // Seconds can be followed by milliseconds OR by the frame information
+      var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
+          errorMessage = "Invalid time format",
+          digitPairs, lastIndex, lastPair, firstPair,
+          frameInfo, frameTime;
+
+      if ( typeof timeStr === "number" ) {
+        return timeStr;
+      }
+
+      if ( typeof timeStr === "string" &&
+            !validTimeFormat.test( timeStr ) ) {
+        Popcorn.error( errorMessage );
+      }
+
+      digitPairs = timeStr.split( ":" );
+      lastIndex = digitPairs.length - 1;
+      lastPair = digitPairs[ lastIndex ];
+
+      // Fix last element:
+      if ( lastPair.indexOf( ";" ) > -1 ) {
+
+        frameInfo = lastPair.split( ";" );
+        frameTime = 0;
+
+        if ( framerate && ( typeof framerate === "number" ) ) {
+          frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
+        }
+
+        digitPairs[ lastIndex ] = parseInt( frameInfo[ 0 ], 10 ) + frameTime;
+      }
+
+      firstPair = digitPairs[ 0 ];
+
+      return {
+
+        1: parseFloat( firstPair, 10 ),
+
+        2: ( parseInt( firstPair, 10 ) * 60 ) +
+              parseFloat( digitPairs[ 1 ], 10 ),
+
+        3: ( parseInt( firstPair, 10 ) * 3600 ) +
+            ( parseInt( digitPairs[ 1 ], 10 ) * 60 ) +
+              parseFloat( digitPairs[ 2 ], 10 )
+
+      }[ digitPairs.length || 1 ];
+    }
+  };
+
+
+  // Initialize locale data
+  // Based on http://en.wikipedia.org/wiki/Language_localisation#Language_tags_and_codes
+  function initLocale( arg ) {
+
+    var locale = typeof arg === "string" ? arg : [ arg.language, arg.region ].join( "-" ),
+        parts = locale.split( "-" );
+
+    // Setup locale data table
+    return {
+      iso6391: locale,
+      language: parts[ 0 ] || "",
+      region: parts[ 1 ] || ""
+    };
+  }
+
+  // Declare locale data table
+  var localeData = initLocale( global.navigator.userLanguage || global.navigator.language );
+
+  Popcorn.locale = {
+
+    // Popcorn.locale.get()
+    // returns reference to privately
+    // defined localeData
+    get: function() {
+      return localeData;
+    },
+
+    // Popcorn.locale.set( string|object );
+    set: function( arg ) {
+
+      localeData = initLocale( arg );
+
+      Popcorn.locale.broadcast();
+
+      return localeData;
+    },
+
+    // Popcorn.locale.broadcast( type )
+    // Sends events to all popcorn media instances that are
+    // listening for locale events
+    broadcast: function( type ) {
+
+      var instances = Popcorn.instances,
+          length = instances.length,
+          idx = 0,
+          instance;
+
+      type = type || "locale:changed";
+
+      // Iterate all current instances
+      for ( ; idx < length; idx++ ) {
+        instance = instances[ idx ];
+
+        // For those instances with locale event listeners,
+        // trigger a locale change event
+        if ( type in instance.data.events  ) {
+          instance.trigger( type );
+        }
+      }
+    }
+  };
+
+  // alias for exec function
+  Popcorn.p.cue = Popcorn.p.exec;
+
+  function getItems() {
+
+    var item,
+        list = [];
+
+    if ( Object.keys ) {
+      list = Object.keys( Popcorn.p );
+    } else {
+
+      for ( item in Popcorn.p ) {
+        if ( hasOwn.call( Popcorn.p, item ) ) {
+          list.push( item );
+        }
+      }
+    }
+
+    return list.join( "," ).toLowerCase().split( ",");
+  }
+
+  //  Protected API methods
+  Popcorn.protect = {
+    natives: getItems()
+  };
+
+  //  Exposes Popcorn to global context
+  global.Popcorn = Popcorn;
+
+})(window, window.document);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/popcorn.jwplayer.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,111 @@
+var jwplayerObjects = {};
+
+Popcorn.player( "jwplayer", {
+  _setup: function( options ) {
+
+    var media = this,
+        player = {},
+        container = document.createElement( "div" ),
+        currentTime = 0,
+        seekTime = 0,
+        seeking = false,
+        dataLoaded = false;
+    container.id = media.id + Popcorn.guid();
+
+    media.appendChild( container );
+
+  var initApi = function () {
+    jwplayer( container.id ).onTime(function() {
+        currentTime = jwplayer(container.id).getPosition();
+        media.dispatchEvent( "timeupdate" );
+       // timeout = setTimeout( timeupdate, 10 );
+    });
+    
+    media.play = function() {
+      media.paused = false;
+      media.dispatchEvent( "play" );
+
+      media.dispatchEvent( "playing" );
+      jwplayer( container.id ).play();
+    };
+    
+    media.pause = function() {
+
+      if ( !media.paused ) {
+        media.paused = true;
+        media.dispatchEvent( "pause" );
+        jwplayer( container.id ).pause();
+      }
+    };
+
+    Popcorn.player.defineProperty( media, "currentTime", {
+          set: function( val ) {
+            // make sure val is a number
+            currentTime = seekTime = +val;
+            seeking = true;
+            media.dispatchEvent( "seeked" );
+            media.dispatchEvent( "timeupdate" );
+            jwplayer( container.id ).seek( currentTime );
+            return currentTime;
+          },
+          get: function() {
+            return jwplayer( container.id ).getPosition();            
+          }
+        });
+ 
+    Popcorn.player.defineProperty( media, "muted", {   
+        set: function( val ) {
+          if ( jwplayer( container.id ).getMute() !== val ) {
+            if ( val ) {
+              jwplayer( container.id ).setMute(true);
+            } else {
+              jwplayer( container.id ).setMute(false);
+            }
+
+            media.dispatchEvent( "volumechange" );
+          }
+          
+          return jwplayer( container.id ).getMute();
+        },
+        get: function() {
+          return jwplayer( container.id ).getMute();
+        }
+    });
+  
+    Popcorn.player.defineProperty( media, "volume", {
+    
+      set: function( val ) {
+
+        if ( jwplayer( container.id ).getVolume() !== val *100 ) {
+          jwplayer( container.id ).setVolume( val * 100);
+          media.dispatchEvent( "volumechange" );
+        }
+        
+        return (jwplayer( container.id ).getVolume()) / 100;
+      },
+      
+      get: function() {
+        return jwplayer( container.id ).getVolume() / 100;
+      }
+    });
+
+    media.readyState = 4;
+    media.dispatchEvent( 'load' );
+    dataLoaded = true;
+
+    media.duration = options.duration;
+    media.dispatchEvent( 'durationchange' );
+
+    media.paused && media.dispatchEvent( 'loadeddata' );
+
+    };
+
+    options.events = {
+        onReady: initApi
+      };
+      
+    jwplayer( container.id ).setup(options);
+
+  }
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/popcorn.youtube.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,220 @@
+// A global callback for youtube... that makes me angry
+var onYouTubePlayerReady = function( containerId ) {
+
+  onYouTubePlayerReady[ containerId ] && onYouTubePlayerReady[ containerId ]();
+};
+onYouTubePlayerReady.stateChangeEventHandler = {};
+
+Popcorn.player( "youtube", {
+  _setup: function( options ) {
+
+    var media = this,
+        youtubeObject,
+        container = document.createElement( "div" ),
+        currentTime = 0,
+        seekTime = 0,
+        seeking = false,
+
+        // state code for volume changed polling
+        volumeChanged = false,
+        lastMuted = false,
+        lastVolume = 0;
+
+    container.id = media.id + Popcorn.guid();
+
+    media.appendChild( container );
+
+    var youtubeInit = function() {
+
+      var flashvars,
+          params,
+          attributes,
+          src;
+
+      // expose a callback to this scope, that is called from the global callback youtube calls
+      onYouTubePlayerReady[ container.id ] = function() {
+
+        youtubeObject = document.getElementById( container.id );
+
+        // more youtube callback nonsense
+        onYouTubePlayerReady.stateChangeEventHandler[ container.id ] = function( state ) {
+
+          // playing is state 1
+          // paused is state 2
+          if ( state === 1 ) {
+
+            media.paused && media.play();
+          // youtube fires paused events while seeking
+          // this is the only way to get seeking events
+          } else if ( state === 2 ) {
+
+            // silly logic forced on me by the youtube API
+            // calling youtube.seekTo triggers multiple events
+            // with the second events getCurrentTime being the old time
+            if ( seeking && seekTime === currentTime && seekTime !== youtubeObject.getCurrentTime() ) {
+
+              seeking = false;
+              youtubeObject.seekTo( currentTime );
+              return;
+            }
+
+            currentTime = youtubeObject.getCurrentTime();
+            media.dispatchEvent( "timeupdate" );
+            !media.paused && media.pause();
+          }
+        };
+
+        // youtube requires callbacks to be a string to a function path from the global scope
+        youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id );
+
+        var timeupdate = function() {
+
+          if ( !media.paused ) {
+
+            currentTime = youtubeObject.getCurrentTime();
+            media.dispatchEvent( "timeupdate" );
+            setTimeout( timeupdate, 10 );
+          }
+        };
+
+        var volumeupdate = function() {
+
+          if ( lastMuted !== youtubeObject.isMuted() ) {
+
+            lastMuted = youtubeObject.isMuted();
+            media.dispatchEvent( "volumechange" );
+          }
+
+          if ( lastVolume !== youtubeObject.getVolume() ) {
+
+            lastVolume = youtubeObject.getVolume();
+            media.dispatchEvent( "volumechange" );
+          }
+
+          setTimeout( volumeupdate, 250 );
+        };
+
+        media.play = function() {
+
+          media.paused = false;
+          media.dispatchEvent( "play" );
+
+          media.dispatchEvent( "playing" );
+          timeupdate();
+          youtubeObject.playVideo();
+        };
+
+        media.pause = function() {
+
+          if ( !media.paused ) {
+
+            media.paused = true;
+            media.dispatchEvent( "pause" );
+            youtubeObject.pauseVideo();
+          }
+        };
+
+        Popcorn.player.defineProperty( media, "currentTime", {
+          set: function( val ) {
+
+            // make sure val is a number
+            currentTime = seekTime = +val;
+            seeking = true;
+            media.dispatchEvent( "seeked" );
+            media.dispatchEvent( "timeupdate" );
+            youtubeObject.seekTo( currentTime );
+            return currentTime;
+          },
+          get: function() {
+
+            return currentTime;
+          }
+        });
+
+        Popcorn.player.defineProperty( media, "muted", {
+          set: function( val ) {
+
+            if ( youtubeObject.isMuted() !== val ) {
+
+              if ( val ) {
+
+                youtubeObject.mute();
+              } else {
+
+                youtubeObject.unMute();
+              }
+
+              lastMuted = youtubeObject.isMuted();
+              media.dispatchEvent( "volumechange" );
+            }
+
+            return youtubeObject.isMuted();
+          },
+          get: function() {
+
+            return youtubeObject.isMuted();
+          }
+        });
+
+        Popcorn.player.defineProperty( media, "volume", {
+          set: function( val ) {
+
+            if ( youtubeObject.getVolume() !== val ) {
+
+              youtubeObject.setVolume( val );
+              lastVolume = youtubeObject.getVolume();
+              media.dispatchEvent( "volumechange" );
+            }
+
+            return youtubeObject.getVolume();
+          },
+          get: function() {
+
+            return youtubeObject.getVolume();
+          }
+        });
+
+        media.readyState = 4;
+        media.dispatchEvent( "load" );
+        media.duration = youtubeObject.getDuration();
+        media.dispatchEvent( "durationchange" );
+        volumeupdate();
+
+        media.dispatchEvent( "loadeddata" );
+      };
+
+      options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1;
+      options.annotations = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1;
+
+      flashvars = {
+        playerapiid: container.id,
+        controls: options.controls,
+        iv_load_policy: options.annotations
+      };
+
+      params = {
+        wmode: "transparent",
+        allowScriptAccess: "always"
+      };
+
+      attributes = {
+        id: container.id
+      };
+
+      src = /^.*[\/=](.{11})/.exec( media.src )[ 1 ];
+
+      swfobject.embedSWF( "http://www.youtube.com/e/" + src + "?enablejsapi=1&playerapiid=" + container.id + "&version=3",
+                          container.id, media.offsetWidth, media.offsetHeight, "8", null,
+                          flashvars, params, attributes );
+    };
+
+    if ( !window.swfobject ) {
+
+      Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", youtubeInit );
+    } else {
+
+      youtubeInit();
+    }
+  }
+});
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/raphael.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,5436 @@
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ Raphaël 2.0 - JavaScript Vector Library                             │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+
+// ┌──────────────────────────────────────────────────────────────────────────────────────┐ \\
+// │ Eve 0.3.2 - JavaScript Events Library                                                │ \\
+// ├──────────────────────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/)          │ \\
+// │ Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. │ \\
+// └──────────────────────────────────────────────────────────────────────────────────────┘ \\
+
+(function (glob) {
+    var version = "0.3.2",
+        has = "hasOwnProperty",
+        separator = /[\.\/]/,
+        wildcard = "*",
+        fun = function () {},
+        numsort = function (a, b) {
+            return a - b;
+        },
+        current_event,
+        stop,
+        events = {n: {}},
+    
+        eve = function (name, scope) {
+            var e = events,
+                oldstop = stop,
+                args = Array.prototype.slice.call(arguments, 2),
+                listeners = eve.listeners(name),
+                z = 0,
+                f = false,
+                l,
+                indexed = [],
+                queue = {},
+                out = [],
+                errors = [];
+            current_event = name;
+            stop = 0;
+            for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
+                indexed.push(listeners[i].zIndex);
+                if (listeners[i].zIndex < 0) {
+                    queue[listeners[i].zIndex] = listeners[i];
+                }
+            }
+            indexed.sort(numsort);
+            while (indexed[z] < 0) {
+                l = queue[indexed[z++]];
+                out.push(l.apply(scope, args));
+                if (stop) {
+                    stop = oldstop;
+                    return out;
+                }
+            }
+            for (i = 0; i < ii; i++) {
+                l = listeners[i];
+                if ("zIndex" in l) {
+                    if (l.zIndex == indexed[z]) {
+                        out.push(l.apply(scope, args));
+                        if (stop) {
+                            stop = oldstop;
+                            return out;
+                        }
+                        do {
+                            z++;
+                            l = queue[indexed[z]];
+                            l && out.push(l.apply(scope, args));
+                            if (stop) {
+                                stop = oldstop;
+                                return out;
+                            }
+                        } while (l)
+                    } else {
+                        queue[l.zIndex] = l;
+                    }
+                } else {
+                    out.push(l.apply(scope, args));
+                    if (stop) {
+                        stop = oldstop;
+                        return out;
+                    }
+                }
+            }
+            stop = oldstop;
+            return out.length ? out : null;
+        };
+    
+    eve.listeners = function (name) {
+        var names = name.split(separator),
+            e = events,
+            item,
+            items,
+            k,
+            i,
+            ii,
+            j,
+            jj,
+            nes,
+            es = [e],
+            out = [];
+        for (i = 0, ii = names.length; i < ii; i++) {
+            nes = [];
+            for (j = 0, jj = es.length; j < jj; j++) {
+                e = es[j].n;
+                items = [e[names[i]], e[wildcard]];
+                k = 2;
+                while (k--) {
+                    item = items[k];
+                    if (item) {
+                        nes.push(item);
+                        out = out.concat(item.f || []);
+                    }
+                }
+            }
+            es = nes;
+        }
+        return out;
+    };
+    
+    
+    eve.on = function (name, f) {
+        var names = name.split(separator),
+            e = events;
+        for (var i = 0, ii = names.length; i < ii; i++) {
+            e = e.n;
+            !e[names[i]] && (e[names[i]] = {n: {}});
+            e = e[names[i]];
+        }
+        e.f = e.f || [];
+        for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
+            return fun;
+        }
+        e.f.push(f);
+        return function (zIndex) {
+            if (+zIndex == +zIndex) {
+                f.zIndex = +zIndex;
+            }
+        };
+    };
+    
+    eve.stop = function () {
+        stop = 1;
+    };
+    
+    eve.nt = function (subname) {
+        if (subname) {
+            return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
+        }
+        return current_event;
+    };
+    
+    eve.unbind = function (name, f) {
+        var names = name.split(separator),
+            e,
+            key,
+            splice,
+            cur = [events];
+        for (var i = 0, ii = names.length; i < ii; i++) {
+            for (var j = 0; j < cur.length; j += splice.length - 2) {
+                splice = [j, 1];
+                e = cur[j].n;
+                if (names[i] != wildcard) {
+                    if (e[names[i]]) {
+                        splice.push(e[names[i]]);
+                    }
+                } else {
+                    for (key in e) if (e[has](key)) {
+                        splice.push(e[key]);
+                    }
+                }
+                cur.splice.apply(cur, splice);
+            }
+        }
+        for (i = 0, ii = cur.length; i < ii; i++) {
+            e = cur[i];
+            while (e.n) {
+                if (f) {
+                    if (e.f) {
+                        for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
+                            e.f.splice(j, 1);
+                            break;
+                        }
+                        !e.f.length && delete e.f;
+                    }
+                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
+                        var funcs = e.n[key].f;
+                        for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
+                            funcs.splice(j, 1);
+                            break;
+                        }
+                        !funcs.length && delete e.n[key].f;
+                    }
+                } else {
+                    delete e.f;
+                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
+                        delete e.n[key].f;
+                    }
+                }
+                e = e.n;
+            }
+        }
+    };
+    
+    eve.version = version;
+    eve.toString = function () {
+        return "You are running Eve " + version;
+    };
+    (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (glob.eve = eve);
+})(this);
+
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ "Raphaël 2.0" - JavaScript Vector Library                           │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+(function () {
+    
+    function R(first) {
+        if (R.is(first, "function")) {
+            return loaded ? first() : eve.on("DOMload", first);
+        } else if (R.is(first, array)) {
+            var a = first,
+                cnv = R._engine.create[apply](R, a.splice(0, 3 + R.is(a[0], nu))),
+                res = cnv.set(),
+                i = 0,
+                ii = a.length,
+                j;
+            for (; i < ii; i++) {
+                j = a[i] || {};
+                elements[has](j.type) && res.push(cnv[j.type]().attr(j));
+            }
+            return res;
+        } else {
+            var args = Array.prototype.slice.call(arguments, 0);
+            if (R.is(args[args.length - 1], "function")) {
+                var f = args.pop();
+                return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("DOMload", function () {
+                    f.call(R._engine.create[apply](R, args));
+                });
+            } else {
+                return R._engine.create[apply](R, arguments);
+            }
+        }
+    }
+    R.version = "2.0.0";
+    R.eve = eve;
+    var loaded,
+        separator = /[, ]+/,
+        elements = {circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1},
+        formatrg = /\{(\d+)\}/g,
+        proto = "prototype",
+        has = "hasOwnProperty",
+        g = {
+            doc: document,
+            win: window
+        },
+        oldRaphael = {
+            was: Object.prototype[has].call(g.win, "Raphael"),
+            is: g.win.Raphael
+        },
+        Paper = function () {
+            
+            
+            this.ca = this.customAttributes = {};
+        },
+        paperproto,
+        appendChild = "appendChild",
+        apply = "apply",
+        concat = "concat",
+        supportsTouch = "createTouch" in g.doc,
+        E = "",
+        S = " ",
+        Str = String,
+        split = "split",
+        events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[split](S),
+        touchMap = {
+            mousedown: "touchstart",
+            mousemove: "touchmove",
+            mouseup: "touchend"
+        },
+        lowerCase = Str.prototype.toLowerCase,
+        math = Math,
+        mmax = math.max,
+        mmin = math.min,
+        abs = math.abs,
+        pow = math.pow,
+        PI = math.PI,
+        nu = "number",
+        string = "string",
+        array = "array",
+        toString = "toString",
+        fillString = "fill",
+        objectToString = Object.prototype.toString,
+        paper = {},
+        push = "push",
+        ISURL = R._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
+        colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
+        isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
+        bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
+        round = math.round,
+        setAttribute = "setAttribute",
+        toFloat = parseFloat,
+        toInt = parseInt,
+        upperCase = Str.prototype.toUpperCase,
+        availableAttrs = R._availableAttrs = {
+            "arrow-end": "none",
+            "arrow-start": "none",
+            blur: 0,
+            "clip-rect": "0 0 1e9 1e9",
+            cursor: "default",
+            cx: 0,
+            cy: 0,
+            fill: "#fff",
+            "fill-opacity": 1,
+            font: '10px "Arial"',
+            "font-family": '"Arial"',
+            "font-size": "10",
+            "font-style": "normal",
+            "font-weight": 400,
+            gradient: 0,
+            height: 0,
+            href: "http://raphaeljs.com/",
+            opacity: 1,
+            path: "M0,0",
+            r: 0,
+            rx: 0,
+            ry: 0,
+            src: "",
+            stroke: "#000",
+            "stroke-dasharray": "",
+            "stroke-linecap": "butt",
+            "stroke-linejoin": "butt",
+            "stroke-miterlimit": 0,
+            "stroke-opacity": 1,
+            "stroke-width": 1,
+            target: "_blank",
+            "text-anchor": "middle",
+            title: "Raphael",
+            transform: "",
+            width: 0,
+            x: 0,
+            y: 0
+        },
+        availableAnimAttrs = R._availableAnimAttrs = {
+            blur: nu,
+            "clip-rect": "csv",
+            cx: nu,
+            cy: nu,
+            fill: "colour",
+            "fill-opacity": nu,
+            "font-size": nu,
+            height: nu,
+            opacity: nu,
+            path: "path",
+            r: nu,
+            rx: nu,
+            ry: nu,
+            stroke: "colour",
+            "stroke-opacity": nu,
+            "stroke-width": nu,
+            transform: "transform",
+            width: nu,
+            x: nu,
+            y: nu
+        },
+        commaSpaces = /\s*,\s*/,
+        hsrg = {hs: 1, rg: 1},
+        p2s = /,?([achlmqrstvxz]),?/gi,
+        pathCommand = /([achlmrqstvz])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
+        tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
+        pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)\s*,?\s*/ig,
+        radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,
+        eldata = {},
+        sortByKey = function (a, b) {
+            return a.key - b.key;
+        },
+        sortByNumber = function (a, b) {
+            return toFloat(a) - toFloat(b);
+        },
+        fun = function () {},
+        pipe = function (x) {
+            return x;
+        },
+        rectPath = R._rectPath = function (x, y, w, h, r) {
+            if (r) {
+                return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
+            }
+            return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
+        },
+        ellipsePath = function (x, y, rx, ry) {
+            if (ry == null) {
+                ry = rx;
+            }
+            return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
+        },
+        getPath = R._getPath = {
+            path: function (el) {
+                return el.attr("path");
+            },
+            circle: function (el) {
+                var a = el.attrs;
+                return ellipsePath(a.cx, a.cy, a.r);
+            },
+            ellipse: function (el) {
+                var a = el.attrs;
+                return ellipsePath(a.cx, a.cy, a.rx, a.ry);
+            },
+            rect: function (el) {
+                var a = el.attrs;
+                return rectPath(a.x, a.y, a.width, a.height, a.r);
+            },
+            image: function (el) {
+                var a = el.attrs;
+                return rectPath(a.x, a.y, a.width, a.height);
+            },
+            text: function (el) {
+                var bbox = el._getBBox();
+                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
+            }
+        },
+        mapPath = R.mapPath = function (path, matrix) {
+            if (!matrix) {
+                return path;
+            }
+            var x, y, i, j, pathi;
+            path = path2curve(path);
+            for (i = 0, ii = path.length; i < ii; i++) {
+                pathi = path[i];
+                for (j = 1, jj = pathi.length; j < jj; j += 2) {
+                    x = matrix.x(pathi[j], pathi[j + 1]);
+                    y = matrix.y(pathi[j], pathi[j + 1]);
+                    pathi[j] = x;
+                    pathi[j + 1] = y;
+                }
+            }
+            return path;
+        };
+
+    R._g = g;
+    
+    R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
+    if (R.type == "VML") {
+        var d = g.doc.createElement("div"),
+            b;
+        d.innerHTML = '<v:shape adj="1"/>';
+        b = d.firstChild;
+        b.style.behavior = "url(#default#VML)";
+        if (!(b && typeof b.adj == "object")) {
+            return (R.type = E);
+        }
+        d = null;
+    }
+    
+    
+    R.svg = !(R.vml = R.type == "VML");
+    R._Paper = Paper;
+    
+    R.fn = paperproto = Paper.prototype = R.prototype;
+    R._id = 0;
+    R._oid = 0;
+    
+    R.is = function (o, type) {
+        type = lowerCase.call(type);
+        if (type == "finite") {
+            return !isnan[has](+o);
+        }
+        if (type == "array") {
+            return o instanceof Array;
+        }
+        return  (type == "null" && o === null) ||
+                (type == typeof o && o !== null) ||
+                (type == "object" && o === Object(o)) ||
+                (type == "array" && Array.isArray && Array.isArray(o)) ||
+                objectToString.call(o).slice(8, -1).toLowerCase() == type;
+    };
+    
+    R.angle = function (x1, y1, x2, y2, x3, y3) {
+        if (x3 == null) {
+            var x = x1 - x2,
+                y = y1 - y2;
+            if (!x && !y) {
+                return 0;
+            }
+            return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
+        } else {
+            return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
+        }
+    };
+    
+    R.rad = function (deg) {
+        return deg % 360 * PI / 180;
+    };
+    
+    R.deg = function (rad) {
+        return rad * 180 / PI % 360;
+    };
+    
+    R.snapTo = function (values, value, tolerance) {
+        tolerance = R.is(tolerance, "finite") ? tolerance : 10;
+        if (R.is(values, array)) {
+            var i = values.length;
+            while (i--) if (abs(values[i] - value) <= tolerance) {
+                return values[i];
+            }
+        } else {
+            values = +values;
+            var rem = value % values;
+            if (rem < tolerance) {
+                return value - rem;
+            }
+            if (rem > values - tolerance) {
+                return value - rem + values;
+            }
+        }
+        return value;
+    };
+    
+    
+    var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) {
+        return function () {
+            return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
+        };
+    })(/[xy]/g, function (c) {
+        var r = math.random() * 16 | 0,
+            v = c == "x" ? r : (r & 3 | 8);
+        return v.toString(16);
+    });
+
+    
+    R.setWindow = function (newwin) {
+        eve("setWindow", R, g.win, newwin);
+        g.win = newwin;
+        g.doc = g.win.document;
+        if (initWin) {
+            initWin(g.win);
+        }
+    };
+    var toHex = function (color) {
+        if (R.vml) {
+            // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
+            var trim = /^\s+|\s+$/g;
+            var bod;
+            try {
+                var docum = new ActiveXObject("htmlfile");
+                docum.write("<body>");
+                docum.close();
+                bod = docum.body;
+            } catch(e) {
+                bod = createPopup().document.body;
+            }
+            var range = bod.createTextRange();
+            toHex = cacher(function (color) {
+                try {
+                    bod.style.color = Str(color).replace(trim, E);
+                    var value = range.queryCommandValue("ForeColor");
+                    value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
+                    return "#" + ("000000" + value.toString(16)).slice(-6);
+                } catch(e) {
+                    return "none";
+                }
+            });
+        } else {
+            var i = g.doc.createElement("i");
+            i.title = "Rapha\xebl Colour Picker";
+            i.style.display = "none";
+            g.doc.body.appendChild(i);
+            toHex = cacher(function (color) {
+                i.style.color = color;
+                return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
+            });
+        }
+        return toHex(color);
+    },
+    hsbtoString = function () {
+        return "hsb(" + [this.h, this.s, this.b] + ")";
+    },
+    hsltoString = function () {
+        return "hsl(" + [this.h, this.s, this.l] + ")";
+    },
+    rgbtoString = function () {
+        return this.hex;
+    },
+    prepareRGB = function (r, g, b) {
+        if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
+            b = r.b;
+            g = r.g;
+            r = r.r;
+        }
+        if (g == null && R.is(r, string)) {
+            var clr = R.getRGB(r);
+            r = clr.r;
+            g = clr.g;
+            b = clr.b;
+        }
+        if (r > 1 || g > 1 || b > 1) {
+            r /= 255;
+            g /= 255;
+            b /= 255;
+        }
+        
+        return [r, g, b];
+    },
+    packageRGB = function (r, g, b, o) {
+        r *= 255;
+        g *= 255;
+        b *= 255;
+        var rgb = {
+            r: r,
+            g: g,
+            b: b,
+            hex: R.rgb(r, g, b),
+            toString: rgbtoString
+        };
+        R.is(o, "finite") && (rgb.opacity = o);
+        return rgb;
+    };
+    
+    
+    R.color = function (clr) {
+        var rgb;
+        if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
+            rgb = R.hsb2rgb(clr);
+            clr.r = rgb.r;
+            clr.g = rgb.g;
+            clr.b = rgb.b;
+            clr.hex = rgb.hex;
+        } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
+            rgb = R.hsl2rgb(clr);
+            clr.r = rgb.r;
+            clr.g = rgb.g;
+            clr.b = rgb.b;
+            clr.hex = rgb.hex;
+        } else {
+            if (R.is(clr, "string")) {
+                clr = R.getRGB(clr);
+            }
+            if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
+                rgb = R.rgb2hsl(clr);
+                clr.h = rgb.h;
+                clr.s = rgb.s;
+                clr.l = rgb.l;
+                rgb = R.rgb2hsb(clr);
+                clr.v = rgb.b;
+            } else {
+                clr = {hex: "none"};
+                crl.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
+            }
+        }
+        clr.toString = rgbtoString;
+        return clr;
+    };
+    
+    R.hsb2rgb = function (h, s, v, o) {
+        if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
+            v = h.b;
+            s = h.s;
+            h = h.h;
+            o = h.o;
+        }
+        h *= 360;
+        var R, G, B, X, C;
+        h = (h % 360) / 60;
+        C = v * s;
+        X = C * (1 - abs(h % 2 - 1));
+        R = G = B = v - C;
+
+        h = ~~h;
+        R += [C, X, 0, 0, X, C][h];
+        G += [X, C, C, X, 0, 0][h];
+        B += [0, 0, X, C, C, X][h];
+        return packageRGB(R, G, B, o);
+    };
+    
+    R.hsl2rgb = function (h, s, l, o) {
+        if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
+            l = h.l;
+            s = h.s;
+            h = h.h;
+        }
+        if (h > 1 || s > 1 || l > 1) {
+            h /= 360;
+            s /= 100;
+            l /= 100;
+        }
+        h *= 360;
+        var R, G, B, X, C;
+        h = (h % 360) / 60;
+        C = 2 * s * (l < .5 ? l : 1 - l);
+        X = C * (1 - abs(h % 2 - 1));
+        R = G = B = l - C / 2;
+
+        h = ~~h;
+        R += [C, X, 0, 0, X, C][h];
+        G += [X, C, C, X, 0, 0][h];
+        B += [0, 0, X, C, C, X][h];
+        return packageRGB(R, G, B, o);
+    };
+    
+    R.rgb2hsb = function (r, g, b) {
+        b = prepareRGB(r, g, b);
+        r = b[0];
+        g = b[1];
+        b = b[2];
+
+        var H, S, V, C;
+        V = mmax(r, g, b);
+        C = V - mmin(r, g, b);
+        H = (C == 0 ? null :
+             V == r ? (g - b) / C :
+             V == g ? (b - r) / C + 2 :
+                      (r - g) / C + 4
+            );
+        H = ((H + 360) % 6) * 60 / 360;
+        S = C == 0 ? 0 : C / V;
+        return {h: H, s: S, b: V, toString: hsbtoString};
+    };
+    
+    R.rgb2hsl = function (r, g, b) {
+        b = prepareRGB(r, g, b);
+        r = b[0];
+        g = b[1];
+        b = b[2];
+
+        var H, S, L, M, m, C;
+        M = mmax(r, g, b);
+        m = mmin(r, g, b);
+        C = M - m;
+        H = (C == 0 ? null :
+             M == r ? (g - b) / C :
+             M == g ? (b - r) / C + 2 :
+                      (r - g) / C + 4);
+        H = ((H + 360) % 6) * 60 / 360;
+        L = (M + m) / 2;
+        S = (C == 0 ? 0 :
+             L < .5 ? C / (2 * L) :
+                      C / (2 - 2 * L));
+        return {h: H, s: S, l: L, toString: hsltoString};
+    };
+    R._path2string = function () {
+        return this.join(",").replace(p2s, "$1");
+    };
+    function repush(array, item) {
+        for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
+            return array.push(array.splice(i, 1)[0]);
+        }
+    }
+    function cacher(f, scope, postprocessor) {
+        function newf() {
+            var arg = Array.prototype.slice.call(arguments, 0),
+                args = arg.join("\u2400"),
+                cache = newf.cache = newf.cache || {},
+                count = newf.count = newf.count || [];
+            if (cache[has](args)) {
+                repush(count, args);
+                return postprocessor ? postprocessor(cache[args]) : cache[args];
+            }
+            count.length >= 1e3 && delete cache[count.shift()];
+            count.push(args);
+            cache[args] = f[apply](scope, arg);
+            return postprocessor ? postprocessor(cache[args]) : cache[args];
+        }
+        return newf;
+    }
+
+    var preload = R._preload = function (src, f) {
+        var img = g.doc.createElement("img");
+        img.style.cssText = "position:absolute;left:-9999em;top-9999em";
+        img.onload = function () {
+            f.call(this);
+            this.onload = null;
+            g.doc.body.removeChild(this);
+        };
+        img.onerror = function () {
+            g.doc.body.removeChild(this);
+        };
+        g.doc.body.appendChild(img);
+        img.src = src;
+    };
+    
+    function clrToString() {
+        return this.hex;
+    }
+
+    
+    R.getRGB = cacher(function (colour) {
+        if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
+            return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
+        }
+        if (colour == "none") {
+            return {r: -1, g: -1, b: -1, hex: "none", toString: clrToString};
+        }
+        !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
+        var res,
+            red,
+            green,
+            blue,
+            opacity,
+            t,
+            values,
+            rgb = colour.match(colourRegExp);
+        if (rgb) {
+            if (rgb[2]) {
+                blue = toInt(rgb[2].substring(5), 16);
+                green = toInt(rgb[2].substring(3, 5), 16);
+                red = toInt(rgb[2].substring(1, 3), 16);
+            }
+            if (rgb[3]) {
+                blue = toInt((t = rgb[3].charAt(3)) + t, 16);
+                green = toInt((t = rgb[3].charAt(2)) + t, 16);
+                red = toInt((t = rgb[3].charAt(1)) + t, 16);
+            }
+            if (rgb[4]) {
+                values = rgb[4][split](commaSpaces);
+                red = toFloat(values[0]);
+                values[0].slice(-1) == "%" && (red *= 2.55);
+                green = toFloat(values[1]);
+                values[1].slice(-1) == "%" && (green *= 2.55);
+                blue = toFloat(values[2]);
+                values[2].slice(-1) == "%" && (blue *= 2.55);
+                rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
+                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+            }
+            if (rgb[5]) {
+                values = rgb[5][split](commaSpaces);
+                red = toFloat(values[0]);
+                values[0].slice(-1) == "%" && (red *= 2.55);
+                green = toFloat(values[1]);
+                values[1].slice(-1) == "%" && (green *= 2.55);
+                blue = toFloat(values[2]);
+                values[2].slice(-1) == "%" && (blue *= 2.55);
+                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
+                rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
+                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+                return R.hsb2rgb(red, green, blue, opacity);
+            }
+            if (rgb[6]) {
+                values = rgb[6][split](commaSpaces);
+                red = toFloat(values[0]);
+                values[0].slice(-1) == "%" && (red *= 2.55);
+                green = toFloat(values[1]);
+                values[1].slice(-1) == "%" && (green *= 2.55);
+                blue = toFloat(values[2]);
+                values[2].slice(-1) == "%" && (blue *= 2.55);
+                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
+                rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
+                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
+                return R.hsl2rgb(red, green, blue, opacity);
+            }
+            rgb = {r: red, g: green, b: blue, toString: clrToString};
+            rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
+            R.is(opacity, "finite") && (rgb.opacity = opacity);
+            return rgb;
+        }
+        return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString};
+    }, R);
+    
+    R.hsb = cacher(function (h, s, b) {
+        return R.hsb2rgb(h, s, b).hex;
+    });
+    
+    R.hsl = cacher(function (h, s, l) {
+        return R.hsl2rgb(h, s, l).hex;
+    });
+    
+    R.rgb = cacher(function (r, g, b) {
+        return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
+    });
+    
+    R.getColor = function (value) {
+        var start = this.getColor.start = this.getColor.start || {h: 0, s: 1, b: value || .75},
+            rgb = this.hsb2rgb(start.h, start.s, start.b);
+        start.h += .075;
+        if (start.h > 1) {
+            start.h = 0;
+            start.s -= .2;
+            start.s <= 0 && (this.getColor.start = {h: 0, s: 1, b: start.b});
+        }
+        return rgb.hex;
+    };
+    
+    R.getColor.reset = function () {
+        delete this.start;
+    };
+
+    // http://schepers.cc/getting-to-the-point
+    function catmullRom2bezier(crp) {
+        var d = [];
+        for (var i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
+            var p = [{x: +crp[i],     y: +crp[i + 1]},
+                     {x: +crp[i],     y: +crp[i + 1]},
+                     {x: +crp[i + 2], y: +crp[i + 3]},
+                     {x: +crp[i + 4], y: +crp[i + 5]}];
+            if (iLen - 4 == i) {
+                p[0] = {x: +crp[i - 2], y: +crp[i - 1]};
+                p[3] = p[2];
+            } else if (i) {
+                p[0] = {x: +crp[i - 2], y: +crp[i - 1]};
+            }
+            d.push(["C",
+                (-p[0].x + 6 * p[1].x + p[2].x) / 6,
+                (-p[0].y + 6 * p[1].y + p[2].y) / 6,
+                (p[1].x + 6 * p[2].x - p[3].x) / 6,
+                (p[1].y + 6*p[2].y - p[3].y) / 6,
+                p[2].x,
+                p[2].y
+            ]);
+        }
+
+        return d;
+    }
+    
+    R.parsePathString = cacher(function (pathString) {
+        if (!pathString) {
+            return null;
+        }
+        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
+            data = [];
+        if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
+            data = pathClone(pathString);
+        }
+        if (!data.length) {
+            Str(pathString).replace(pathCommand, function (a, b, c) {
+                var params = [],
+                    name = b.toLowerCase();
+                c.replace(pathValues, function (a, b) {
+                    b && params.push(+b);
+                });
+                if (name == "m" && params.length > 2) {
+                    data.push([b][concat](params.splice(0, 2)));
+                    name = "l";
+                    b = b == "m" ? "l" : "L";
+                }
+                if (name == "r") {
+                    data.push([b][concat](params));
+                } else while (params.length >= paramCounts[name]) {
+                    data.push([b][concat](params.splice(0, paramCounts[name])));
+                    if (!paramCounts[name]) {
+                        break;
+                    }
+                }
+            });
+        }
+        data.toString = R._path2string;
+        return data;
+    });
+    
+    R.parseTransformString = cacher(function (TString) {
+        if (!TString) {
+            return null;
+        }
+        var paramCounts = {r: 3, s: 4, t: 2, m: 6},
+            data = [];
+        if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
+            data = pathClone(TString);
+        }
+        if (!data.length) {
+            Str(TString).replace(tCommand, function (a, b, c) {
+                var params = [],
+                    name = lowerCase.call(b);
+                c.replace(pathValues, function (a, b) {
+                    b && params.push(+b);
+                });
+                data.push([b][concat](params));
+            });
+        }
+        data.toString = R._path2string;
+        return data;
+    });
+    
+    R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+        var t1 = 1 - t,
+            t13 = pow(t1, 3),
+            t12 = pow(t1, 2),
+            t2 = t * t,
+            t3 = t2 * t,
+            x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
+            y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
+            mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
+            my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
+            nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
+            ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
+            ax = t1 * p1x + t * c1x,
+            ay = t1 * p1y + t * c1y,
+            cx = t1 * c2x + t * p2x,
+            cy = t1 * c2y + t * p2y,
+            alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
+        (mx > nx || my < ny) && (alpha += 180);
+        return {
+            x: x,
+            y: y,
+            m: {x: mx, y: my},
+            n: {x: nx, y: ny},
+            start: {x: ax, y: ay},
+            end: {x: cx, y: cy},
+            alpha: alpha
+        };
+    };
+    var pathDimensions = cacher(function (path) {
+        if (!path) {
+            return {x: 0, y: 0, width: 0, height: 0};
+        }
+        path = path2curve(path);
+        var x = 0, 
+            y = 0,
+            X = [],
+            Y = [],
+            p;
+        for (var i = 0, ii = path.length; i < ii; i++) {
+            p = path[i];
+            if (p[0] == "M") {
+                x = p[1];
+                y = p[2];
+                X.push(x);
+                Y.push(y);
+            } else {
+                var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+                X = X[concat](dim.min.x, dim.max.x);
+                Y = Y[concat](dim.min.y, dim.max.y);
+                x = p[5];
+                y = p[6];
+            }
+        }
+        var xmin = mmin[apply](0, X),
+            ymin = mmin[apply](0, Y);
+        return {
+            x: xmin,
+            y: ymin,
+            width: mmax[apply](0, X) - xmin,
+            height: mmax[apply](0, Y) - ymin
+        };
+    }, null, function (o) {
+        return {
+            x: o.x,
+            y: o.y,
+            width: o.width,
+            height: o.height
+        };
+    }),
+        pathClone = function (pathArray) {
+            var res = [];
+            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
+                pathArray = R.parsePathString(pathArray);
+            }
+            for (var i = 0, ii = pathArray.length; i < ii; i++) {
+                res[i] = [];
+                for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
+                    res[i][j] = pathArray[i][j];
+                }
+            }
+            res.toString = R._path2string;
+            return res;
+        },
+        pathToRelative = R._pathToRelative = cacher(function (pathArray) {
+            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
+                pathArray = R.parsePathString(pathArray);
+            }
+            var res = [],
+                x = 0,
+                y = 0,
+                mx = 0,
+                my = 0,
+                start = 0;
+            if (pathArray[0][0] == "M") {
+                x = pathArray[0][1];
+                y = pathArray[0][2];
+                mx = x;
+                my = y;
+                start++;
+                res.push(["M", x, y]);
+            }
+            for (var i = start, ii = pathArray.length; i < ii; i++) {
+                var r = res[i] = [],
+                    pa = pathArray[i];
+                if (pa[0] != lowerCase.call(pa[0])) {
+                    r[0] = lowerCase.call(pa[0]);
+                    switch (r[0]) {
+                        case "a":
+                            r[1] = pa[1];
+                            r[2] = pa[2];
+                            r[3] = pa[3];
+                            r[4] = pa[4];
+                            r[5] = pa[5];
+                            r[6] = +(pa[6] - x).toFixed(3);
+                            r[7] = +(pa[7] - y).toFixed(3);
+                            break;
+                        case "v":
+                            r[1] = +(pa[1] - y).toFixed(3);
+                            break;
+                        case "m":
+                            mx = pa[1];
+                            my = pa[2];
+                        default:
+                            for (var j = 1, jj = pa.length; j < jj; j++) {
+                                r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
+                            }
+                    }
+                } else {
+                    r = res[i] = [];
+                    if (pa[0] == "m") {
+                        mx = pa[1] + x;
+                        my = pa[2] + y;
+                    }
+                    for (var k = 0, kk = pa.length; k < kk; k++) {
+                        res[i][k] = pa[k];
+                    }
+                }
+                var len = res[i].length;
+                switch (res[i][0]) {
+                    case "z":
+                        x = mx;
+                        y = my;
+                        break;
+                    case "h":
+                        x += +res[i][len - 1];
+                        break;
+                    case "v":
+                        y += +res[i][len - 1];
+                        break;
+                    default:
+                        x += +res[i][len - 2];
+                        y += +res[i][len - 1];
+                }
+            }
+            res.toString = R._path2string;
+            return res;
+        }, 0, pathClone),
+        pathToAbsolute = R._pathToAbsolute = cacher(function (pathArray) {
+            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
+                pathArray = R.parsePathString(pathArray);
+            }
+            if (!pathArray || !pathArray.length) {
+                return [["M", 0, 0]];
+            }
+            var res = [],
+                x = 0,
+                y = 0,
+                mx = 0,
+                my = 0,
+                start = 0;
+            if (pathArray[0][0] == "M") {
+                x = +pathArray[0][1];
+                y = +pathArray[0][2];
+                mx = x;
+                my = y;
+                start++;
+                res[0] = ["M", x, y];
+            }
+            for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
+                res.push(r = []);
+                pa = pathArray[i];
+                if (pa[0] != upperCase.call(pa[0])) {
+                    r[0] = upperCase.call(pa[0]);
+                    switch (r[0]) {
+                        case "A":
+                            r[1] = pa[1];
+                            r[2] = pa[2];
+                            r[3] = pa[3];
+                            r[4] = pa[4];
+                            r[5] = pa[5];
+                            r[6] = +(pa[6] + x);
+                            r[7] = +(pa[7] + y);
+                            break;
+                        case "V":
+                            r[1] = +pa[1] + y;
+                            break;
+                        case "H":
+                            r[1] = +pa[1] + x;
+                            break;
+                        case "R":
+                            var dots = [x, y][concat](pa.slice(1));
+                            for (var j = 2, jj = dots.length; j < jj; j++) {
+                                dots[j] = +dots[j] + x;
+                                dots[++j] = +dots[j] + y;
+                            }
+                            res.pop();
+                            res = res[concat](catmullRom2bezier(dots));
+                            break;
+                        case "M":
+                            mx = +pa[1] + x;
+                            my = +pa[2] + y;
+                        default:
+                            for (j = 1, jj = pa.length; j < jj; j++) {
+                                r[j] = +pa[j] + ((j % 2) ? x : y);
+                            }
+                    }
+                } else if (pa[0] == "R") {
+                    dots = [x, y][concat](pa.slice(1));
+                    res.pop();
+                    res = res[concat](catmullRom2bezier(dots));
+                    r = ["R"][concat](pa.slice(-2));
+                } else {
+                    for (var k = 0, kk = pa.length; k < kk; k++) {
+                        r[k] = pa[k];
+                    }
+                }
+                switch (r[0]) {
+                    case "Z":
+                        x = mx;
+                        y = my;
+                        break;
+                    case "H":
+                        x = r[1];
+                        break;
+                    case "V":
+                        y = r[1];
+                        break;
+                    case "M":
+                        mx = r[r.length - 2];
+                        my = r[r.length - 1];
+                    default:
+                        x = r[r.length - 2];
+                        y = r[r.length - 1];
+                }
+            }
+            res.toString = R._path2string;
+            return res;
+        }, null, pathClone),
+        l2c = function (x1, y1, x2, y2) {
+            return [x1, y1, x2, y2, x2, y2];
+        },
+        q2c = function (x1, y1, ax, ay, x2, y2) {
+            var _13 = 1 / 3,
+                _23 = 2 / 3;
+            return [
+                    _13 * x1 + _23 * ax,
+                    _13 * y1 + _23 * ay,
+                    _13 * x2 + _23 * ax,
+                    _13 * y2 + _23 * ay,
+                    x2,
+                    y2
+                ];
+        },
+        a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
+            // for more information of where this math came from visit:
+            // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+            var _120 = PI * 120 / 180,
+                rad = PI / 180 * (+angle || 0),
+                res = [],
+                xy,
+                rotate = cacher(function (x, y, rad) {
+                    var X = x * math.cos(rad) - y * math.sin(rad),
+                        Y = x * math.sin(rad) + y * math.cos(rad);
+                    return {x: X, y: Y};
+                });
+            if (!recursive) {
+                xy = rotate(x1, y1, -rad);
+                x1 = xy.x;
+                y1 = xy.y;
+                xy = rotate(x2, y2, -rad);
+                x2 = xy.x;
+                y2 = xy.y;
+                var cos = math.cos(PI / 180 * angle),
+                    sin = math.sin(PI / 180 * angle),
+                    x = (x1 - x2) / 2,
+                    y = (y1 - y2) / 2;
+                var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+                if (h > 1) {
+                    h = math.sqrt(h);
+                    rx = h * rx;
+                    ry = h * ry;
+                }
+                var rx2 = rx * rx,
+                    ry2 = ry * ry,
+                    k = (large_arc_flag == sweep_flag ? -1 : 1) *
+                        math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
+                    cx = k * rx * y / ry + (x1 + x2) / 2,
+                    cy = k * -ry * x / rx + (y1 + y2) / 2,
+                    f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
+                    f2 = math.asin(((y2 - cy) / ry).toFixed(9));
+
+                f1 = x1 < cx ? PI - f1 : f1;
+                f2 = x2 < cx ? PI - f2 : f2;
+                f1 < 0 && (f1 = PI * 2 + f1);
+                f2 < 0 && (f2 = PI * 2 + f2);
+                if (sweep_flag && f1 > f2) {
+                    f1 = f1 - PI * 2;
+                }
+                if (!sweep_flag && f2 > f1) {
+                    f2 = f2 - PI * 2;
+                }
+            } else {
+                f1 = recursive[0];
+                f2 = recursive[1];
+                cx = recursive[2];
+                cy = recursive[3];
+            }
+            var df = f2 - f1;
+            if (abs(df) > _120) {
+                var f2old = f2,
+                    x2old = x2,
+                    y2old = y2;
+                f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+                x2 = cx + rx * math.cos(f2);
+                y2 = cy + ry * math.sin(f2);
+                res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
+            }
+            df = f2 - f1;
+            var c1 = math.cos(f1),
+                s1 = math.sin(f1),
+                c2 = math.cos(f2),
+                s2 = math.sin(f2),
+                t = math.tan(df / 4),
+                hx = 4 / 3 * rx * t,
+                hy = 4 / 3 * ry * t,
+                m1 = [x1, y1],
+                m2 = [x1 + hx * s1, y1 - hy * c1],
+                m3 = [x2 + hx * s2, y2 - hy * c2],
+                m4 = [x2, y2];
+            m2[0] = 2 * m1[0] - m2[0];
+            m2[1] = 2 * m1[1] - m2[1];
+            if (recursive) {
+                return [m2, m3, m4][concat](res);
+            } else {
+                res = [m2, m3, m4][concat](res).join()[split](",");
+                var newres = [];
+                for (var i = 0, ii = res.length; i < ii; i++) {
+                    newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
+                }
+                return newres;
+            }
+        },
+        findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+            var t1 = 1 - t;
+            return {
+                x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
+                y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
+            };
+        },
+        curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
+            var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
+                b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
+                c = p1x - c1x,
+                t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
+                t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
+                y = [p1y, p2y],
+                x = [p1x, p2x],
+                dot;
+            abs(t1) > "1e12" && (t1 = .5);
+            abs(t2) > "1e12" && (t2 = .5);
+            if (t1 > 0 && t1 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            if (t2 > 0 && t2 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
+            b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
+            c = p1y - c1y;
+            t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
+            t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
+            abs(t1) > "1e12" && (t1 = .5);
+            abs(t2) > "1e12" && (t2 = .5);
+            if (t1 > 0 && t1 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            if (t2 > 0 && t2 < 1) {
+                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
+                x.push(dot.x);
+                y.push(dot.y);
+            }
+            return {
+                min: {x: mmin[apply](0, x), y: mmin[apply](0, y)},
+                max: {x: mmax[apply](0, x), y: mmax[apply](0, y)}
+            };
+        }),
+        path2curve = R._path2curve = cacher(function (path, path2) {
+            var p = pathToAbsolute(path),
+                p2 = path2 && pathToAbsolute(path2),
+                attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+                attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+                processPath = function (path, d) {
+                    var nx, ny;
+                    if (!path) {
+                        return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
+                    }
+                    !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
+                    switch (path[0]) {
+                        case "M":
+                            d.X = path[1];
+                            d.Y = path[2];
+                            break;
+                        case "A":
+                            path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
+                            break;
+                        case "S":
+                            nx = d.x + (d.x - (d.bx || d.x));
+                            ny = d.y + (d.y - (d.by || d.y));
+                            path = ["C", nx, ny][concat](path.slice(1));
+                            break;
+                        case "T":
+                            d.qx = d.x + (d.x - (d.qx || d.x));
+                            d.qy = d.y + (d.y - (d.qy || d.y));
+                            path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
+                            break;
+                        case "Q":
+                            d.qx = path[1];
+                            d.qy = path[2];
+                            path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
+                            break;
+                        case "L":
+                            path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
+                            break;
+                        case "H":
+                            path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
+                            break;
+                        case "V":
+                            path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
+                            break;
+                        case "Z":
+                            path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
+                            break;
+                    }
+                    return path;
+                },
+                fixArc = function (pp, i) {
+                    if (pp[i].length > 7) {
+                        pp[i].shift();
+                        var pi = pp[i];
+                        while (pi.length) {
+                            pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
+                        }
+                        pp.splice(i, 1);
+                        ii = mmax(p.length, p2 && p2.length || 0);
+                    }
+                },
+                fixM = function (path1, path2, a1, a2, i) {
+                    if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
+                        path2.splice(i, 0, ["M", a2.x, a2.y]);
+                        a1.bx = 0;
+                        a1.by = 0;
+                        a1.x = path1[i][1];
+                        a1.y = path1[i][2];
+                        ii = mmax(p.length, p2 && p2.length || 0);
+                    }
+                };
+            for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
+                p[i] = processPath(p[i], attrs);
+                fixArc(p, i);
+                p2 && (p2[i] = processPath(p2[i], attrs2));
+                p2 && fixArc(p2, i);
+                fixM(p, p2, attrs, attrs2, i);
+                fixM(p2, p, attrs2, attrs, i);
+                var seg = p[i],
+                    seg2 = p2 && p2[i],
+                    seglen = seg.length,
+                    seg2len = p2 && seg2.length;
+                attrs.x = seg[seglen - 2];
+                attrs.y = seg[seglen - 1];
+                attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
+                attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
+                attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
+                attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
+                attrs2.x = p2 && seg2[seg2len - 2];
+                attrs2.y = p2 && seg2[seg2len - 1];
+            }
+            return p2 ? [p, p2] : p;
+        }, null, pathClone),
+        parseDots = R._parseDots = cacher(function (gradient) {
+            var dots = [];
+            for (var i = 0, ii = gradient.length; i < ii; i++) {
+                var dot = {},
+                    par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
+                dot.color = R.getRGB(par[1]);
+                if (dot.color.error) {
+                    return null;
+                }
+                dot.color = dot.color.hex;
+                par[2] && (dot.offset = par[2] + "%");
+                dots.push(dot);
+            }
+            for (i = 1, ii = dots.length - 1; i < ii; i++) {
+                if (!dots[i].offset) {
+                    var start = toFloat(dots[i - 1].offset || 0),
+                        end = 0;
+                    for (var j = i + 1; j < ii; j++) {
+                        if (dots[j].offset) {
+                            end = dots[j].offset;
+                            break;
+                        }
+                    }
+                    if (!end) {
+                        end = 100;
+                        j = ii;
+                    }
+                    end = toFloat(end);
+                    var d = (end - start) / (j - i + 1);
+                    for (; i < j; i++) {
+                        start += d;
+                        dots[i].offset = start + "%";
+                    }
+                }
+            }
+            return dots;
+        }),
+        tear = R._tear = function (el, paper) {
+            el == paper.top && (paper.top = el.prev);
+            el == paper.bottom && (paper.bottom = el.next);
+            el.next && (el.next.prev = el.prev);
+            el.prev && (el.prev.next = el.next);
+        },
+        tofront = R._tofront = function (el, paper) {
+            if (paper.top === el) {
+                return;
+            }
+            tear(el, paper);
+            el.next = null;
+            el.prev = paper.top;
+            paper.top.next = el;
+            paper.top = el;
+        },
+        toback = R._toback = function (el, paper) {
+            if (paper.bottom === el) {
+                return;
+            }
+            tear(el, paper);
+            el.next = paper.bottom;
+            el.prev = null;
+            paper.bottom.prev = el;
+            paper.bottom = el;
+        },
+        insertafter = R._insertafter = function (el, el2, paper) {
+            tear(el, paper);
+            el2 == paper.top && (paper.top = el);
+            el2.next && (el2.next.prev = el);
+            el.next = el2.next;
+            el.prev = el2;
+            el2.next = el;
+        },
+        insertbefore = R._insertbefore = function (el, el2, paper) {
+            tear(el, paper);
+            el2 == paper.bottom && (paper.bottom = el);
+            el2.prev && (el2.prev.next = el);
+            el.prev = el2.prev;
+            el2.prev = el;
+            el.next = el2;
+        },
+        removed = function (methodname) {
+            return function () {
+                throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
+            };
+        },
+        extractTransform = R._extractTransform = function (el, tstr) {
+            if (tstr == null) {
+                return el._.transform;
+            }
+            tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
+            var tdata = R.parseTransformString(tstr),
+                deg = 0,
+                dx = 0,
+                dy = 0,
+                sx = 1,
+                sy = 1,
+                _ = el._,
+                m = new Matrix;
+            _.transform = tdata || [];
+            if (tdata) {
+                for (var i = 0, ii = tdata.length; i < ii; i++) {
+                    var t = tdata[i],
+                        tlen = t.length,
+                        command = Str(t[0]).toLowerCase(),
+                        absolute = t[0] != command,
+                        inver = absolute ? m.invert() : 0,
+                        x1,
+                        y1,
+                        x2,
+                        y2,
+                        bb;
+                    if (command == "t" && tlen == 3) {
+                        if (absolute) {
+                            x1 = inver.x(0, 0);
+                            y1 = inver.y(0, 0);
+                            x2 = inver.x(t[1], t[2]);
+                            y2 = inver.y(t[1], t[2]);
+                            m.translate(x2 - x1, y2 - y1);
+                        } else {
+                            m.translate(t[1], t[2]);
+                        }
+                    } else if (command == "r") {
+                        if (tlen == 2) {
+                            bb = bb || el.getBBox(1);
+                            m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
+                            deg += t[1];
+                        } else if (tlen == 4) {
+                            if (absolute) {
+                                x2 = inver.x(t[2], t[3]);
+                                y2 = inver.y(t[2], t[3]);
+                                m.rotate(t[1], x2, y2);
+                            } else {
+                                m.rotate(t[1], t[2], t[3]);
+                            }
+                            deg += t[1];
+                        }
+                    } else if (command == "s") {
+                        if (tlen == 2 || tlen == 3) {
+                            bb = bb || el.getBBox(1);
+                            m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
+                            sx *= t[1];
+                            sy *= t[tlen - 1];
+                        } else if (tlen == 5) {
+                            if (absolute) {
+                                x2 = inver.x(t[3], t[4]);
+                                y2 = inver.y(t[3], t[4]);
+                                m.scale(t[1], t[2], x2, y2);
+                            } else {
+                                m.scale(t[1], t[2], t[3], t[4]);
+                            }
+                            sx *= t[1];
+                            sy *= t[2];
+                        }
+                    } else if (command == "m" && tlen == 7) {
+                        m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
+                    }
+                    _.dirtyT = 1;
+                    el.matrix = m;
+                }
+            }
+
+            el.matrix = m;
+
+            _.sx = sx;
+            _.sy = sy;
+            _.deg = deg;
+            _.dx = dx = m.e;
+            _.dy = dy = m.f;
+
+            if (sx == 1 && sy == 1 && !deg && _.bbox) {
+                _.bbox.x += +dx;
+                _.bbox.y += +dy;
+            } else {
+                _.dirtyT = 1;
+            }
+        },
+        getEmpty = function (item) {
+            var l = item[0];
+            switch (l.toLowerCase()) {
+                case "t": return [l, 0, 0];
+                case "m": return [l, 1, 0, 0, 1, 0, 0];
+                case "r": if (item.length == 4) {
+                    return [l, 0, item[2], item[3]];
+                } else {
+                    return [l, 0];
+                }
+                case "s": if (item.length == 5) {
+                    return [l, 1, 1, item[3], item[4]];
+                } else if (item.length == 3) {
+                    return [l, 1, 1];
+                } else {
+                    return [l, 1];
+                }
+            }
+        },
+        equaliseTransform = R._equaliseTransform = function (t1, t2) {
+            t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
+            t1 = R.parseTransformString(t1) || [];
+            t2 = R.parseTransformString(t2) || [];
+            var maxlength = mmax(t1.length, t2.length),
+                from = [],
+                to = [],
+                i = 0, j, jj,
+                tt1, tt2;
+            for (; i < maxlength; i++) {
+                tt1 = t1[i] || getEmpty(t2[i]);
+                tt2 = t2[i] || getEmpty(tt1);
+                if ((tt1[0] != tt2[0]) ||
+                    (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
+                    (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
+                    ) {
+                    return;
+                }
+                from[i] = [];
+                to[i] = [];
+                for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
+                    j in tt1 && (from[i][j] = tt1[j]);
+                    j in tt2 && (to[i][j] = tt2[j]);
+                }
+            }
+            return {
+                from: from,
+                to: to
+            };
+        };
+    R._getContainer = function (x, y, w, h) {
+        var container;
+        container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
+        if (container == null) {
+            return;
+        }
+        if (container.tagName) {
+            if (y == null) {
+                return {
+                    container: container,
+                    width: container.style.pixelWidth || container.offsetWidth,
+                    height: container.style.pixelHeight || container.offsetHeight
+                };
+            } else {
+                return {
+                    container: container,
+                    width: y,
+                    height: w
+                };
+            }
+        }
+        return {
+            container: 1,
+            x: x,
+            y: y,
+            width: w,
+            height: h
+        };
+    };
+    
+    R.pathToRelative = pathToRelative;
+    R._engine = {};
+    
+    R.path2curve = path2curve;
+    
+    R.matrix = function (a, b, c, d, e, f) {
+        return new Matrix(a, b, c, d, e, f);
+    };
+    function Matrix(a, b, c, d, e, f) {
+        if (a != null) {
+            this.a = +a;
+            this.b = +b;
+            this.c = +c;
+            this.d = +d;
+            this.e = +e;
+            this.f = +f;
+        } else {
+            this.a = 1;
+            this.b = 0;
+            this.c = 0;
+            this.d = 1;
+            this.e = 0;
+            this.f = 0;
+        }
+    }
+    (function (matrixproto) {
+        
+        matrixproto.add = function (a, b, c, d, e, f) {
+            var out = [[], [], []],
+                m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
+                matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
+                x, y, z, res;
+
+            if (a && a instanceof Matrix) {
+                matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
+            }
+
+            for (x = 0; x < 3; x++) {
+                for (y = 0; y < 3; y++) {
+                    res = 0;
+                    for (z = 0; z < 3; z++) {
+                        res += m[x][z] * matrix[z][y];
+                    }
+                    out[x][y] = res;
+                }
+            }
+            this.a = out[0][0];
+            this.b = out[1][0];
+            this.c = out[0][1];
+            this.d = out[1][1];
+            this.e = out[0][2];
+            this.f = out[1][2];
+        };
+        
+        matrixproto.invert = function () {
+            var me = this,
+                x = me.a * me.d - me.b * me.c;
+            return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
+        };
+        
+        matrixproto.clone = function () {
+            return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
+        };
+        
+        matrixproto.translate = function (x, y) {
+            this.add(1, 0, 0, 1, x, y);
+        };
+        
+        matrixproto.scale = function (x, y, cx, cy) {
+            y == null && (y = x);
+            (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
+            this.add(x, 0, 0, y, 0, 0);
+            (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
+        };
+        
+        matrixproto.rotate = function (a, x, y) {
+            a = R.rad(a);
+            x = x || 0;
+            y = y || 0;
+            var cos = +math.cos(a).toFixed(9),
+                sin = +math.sin(a).toFixed(9);
+            this.add(cos, sin, -sin, cos, x, y);
+            this.add(1, 0, 0, 1, -x, -y);
+        };
+        
+        matrixproto.x = function (x, y) {
+            return x * this.a + y * this.c + this.e;
+        };
+        
+        matrixproto.y = function (x, y) {
+            return x * this.b + y * this.d + this.f;
+        };
+        matrixproto.get = function (i) {
+            return +this[Str.fromCharCode(97 + i)].toFixed(4);
+        };
+        matrixproto.toString = function () {
+            return R.svg ?
+                "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
+                [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
+        };
+        matrixproto.toFilter = function () {
+            return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
+                ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
+                ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
+        };
+        matrixproto.offset = function () {
+            return [this.e.toFixed(4), this.f.toFixed(4)];
+        };
+        function norm(a) {
+            return a[0] * a[0] + a[1] * a[1];
+        }
+        function normalize(a) {
+            var mag = math.sqrt(norm(a));
+            a[0] && (a[0] /= mag);
+            a[1] && (a[1] /= mag);
+        }
+        
+        matrixproto.split = function () {
+            var out = {};
+            // translation
+            out.dx = this.e;
+            out.dy = this.f;
+
+            // scale and shear
+            var row = [[this.a, this.c], [this.b, this.d]];
+            out.scalex = math.sqrt(norm(row[0]));
+            normalize(row[0]);
+
+            out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
+            row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
+
+            out.scaley = math.sqrt(norm(row[1]));
+            normalize(row[1]);
+            out.shear /= out.scaley;
+
+            // rotation
+            var sin = -row[0][1],
+                cos = row[1][1];
+            if (cos < 0) {
+                out.rotate = R.deg(math.acos(cos));
+                if (sin < 0) {
+                    out.rotate = 360 - out.rotate;
+                }
+            } else {
+                out.rotate = R.deg(math.asin(sin));
+            }
+
+            out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
+            out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
+            out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
+            return out;
+        };
+        
+        matrixproto.toTransformString = function (shorter) {
+            var s = shorter || this[split]();
+            if (s.isSimple) {
+                return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0];
+            } else {
+                return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
+            }
+        };
+    })(Matrix.prototype);
+
+    // WebKit rendering bug workaround method
+    var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
+    if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
+        (navigator.vendor == "Google Inc." && version && version[1] < 8)) {
+        
+        paperproto.safari = function () {
+            var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({stroke: "none"});
+            setTimeout(function () {rect.remove();});
+        };
+    } else {
+        paperproto.safari = fun;
+    }
+ 
+    var preventDefault = function () {
+        this.returnValue = false;
+    },
+    preventTouch = function () {
+        return this.originalEvent.preventDefault();
+    },
+    stopPropagation = function () {
+        this.cancelBubble = true;
+    },
+    stopTouch = function () {
+        return this.originalEvent.stopPropagation();
+    },
+    addEvent = (function () {
+        if (g.doc.addEventListener) {
+            return function (obj, type, fn, element) {
+                var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
+                    f = function (e) {
+                        var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+                            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
+                            x = e.clientX + scrollX,
+                            y = e.clientY + scrollY;
+                    if (supportsTouch && touchMap[has](type)) {
+                        for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
+                            if (e.targetTouches[i].target == obj) {
+                                var olde = e;
+                                e = e.targetTouches[i];
+                                e.originalEvent = olde;
+                                e.preventDefault = preventTouch;
+                                e.stopPropagation = stopTouch;
+                                break;
+                            }
+                        }
+                    }
+                    return fn.call(element, e, x, y);
+                };
+                obj.addEventListener(realName, f, false);
+                return function () {
+                    obj.removeEventListener(realName, f, false);
+                    return true;
+                };
+            };
+        } else if (g.doc.attachEvent) {
+            return function (obj, type, fn, element) {
+                var f = function (e) {
+                    e = e || g.win.event;
+                    var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+                        scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
+                        x = e.clientX + scrollX,
+                        y = e.clientY + scrollY;
+                    e.preventDefault = e.preventDefault || preventDefault;
+                    e.stopPropagation = e.stopPropagation || stopPropagation;
+                    return fn.call(element, e, x, y);
+                };
+                obj.attachEvent("on" + type, f);
+                var detacher = function () {
+                    obj.detachEvent("on" + type, f);
+                    return true;
+                };
+                return detacher;
+            };
+        }
+    })(),
+    drag = [],
+    dragMove = function (e) {
+        var x = e.clientX,
+            y = e.clientY,
+            scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
+            dragi,
+            j = drag.length;
+        while (j--) {
+            dragi = drag[j];
+            if (supportsTouch) {
+                var i = e.touches.length,
+                    touch;
+                while (i--) {
+                    touch = e.touches[i];
+                    if (touch.identifier == dragi.el._drag.id) {
+                        x = touch.clientX;
+                        y = touch.clientY;
+                        (e.originalEvent ? e.originalEvent : e).preventDefault();
+                        break;
+                    }
+                }
+            } else {
+                e.preventDefault();
+            }
+            var node = dragi.el.node,
+                o,
+                next = node.nextSibling,
+                parent = node.parentNode,
+                display = node.style.display;
+            g.win.opera && parent.removeChild(node);
+            node.style.display = "none";
+            o = dragi.el.paper.getElementByPoint(x, y);
+            node.style.display = display;
+            g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
+            o && eve("drag.over." + dragi.el.id, dragi.el, o);
+            x += scrollX;
+            y += scrollY;
+            eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
+        }
+    },
+    dragUp = function (e) {
+        R.unmousemove(dragMove).unmouseup(dragUp);
+        var i = drag.length,
+            dragi;
+        while (i--) {
+            dragi = drag[i];
+            dragi.el._drag = {};
+            eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
+        }
+        drag = [];
+    },
+    
+    elproto = R.el = {};
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    for (var i = events.length; i--;) {
+        (function (eventName) {
+            R[eventName] = elproto[eventName] = function (fn, scope) {
+                if (R.is(fn, "function")) {
+                    this.events = this.events || [];
+                    this.events.push({name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this)});
+                }
+                return this;
+            };
+            R["un" + eventName] = elproto["un" + eventName] = function (fn) {
+                var events = this.events,
+                    l = events.length;
+                while (l--) if (events[l].name == eventName && events[l].f == fn) {
+                    events[l].unbind();
+                    events.splice(l, 1);
+                    !events.length && delete this.events;
+                    return this;
+                }
+                return this;
+            };
+        })(events[i]);
+    }
+    
+    
+    elproto.data = function (key, value) {
+        var data = eldata[this.id] = eldata[this.id] || {};
+        if (arguments.length == 1) {
+            if (R.is(key, "object")) {
+                for (var i in key) if (key[has](i)) {
+                    this.data(i, key[i]);
+                }
+                return this;
+            }
+            eve("data.get." + this.id, this, data[key], key);
+            return data[key];
+        }
+        data[key] = value;
+        eve("data.set." + this.id, this, value, key);
+        return this;
+    };
+    
+    elproto.removeData = function (key) {
+        if (key == null) {
+            eldata[this.id] = {};
+        } else {
+            eldata[this.id] && delete eldata[this.id][key];
+        }
+        return this;
+    };
+    
+    elproto.hover = function (f_in, f_out, scope_in, scope_out) {
+        return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
+    };
+    
+    elproto.unhover = function (f_in, f_out) {
+        return this.unmouseover(f_in).unmouseout(f_out);
+    };
+    
+    elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
+        function start(e) {
+            (e.originalEvent || e).preventDefault();
+            var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
+                scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
+            this._drag.x = e.clientX + scrollX;
+            this._drag.y = e.clientY + scrollY;
+            this._drag.id = e.identifier;
+            !drag.length && R.mousemove(dragMove).mouseup(dragUp);
+            drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
+            onstart && eve.on("drag.start." + this.id, onstart);
+            onmove && eve.on("drag.move." + this.id, onmove);
+            onend && eve.on("drag.end." + this.id, onend);
+            eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
+        }
+        this._drag = {};
+        this.mousedown(start);
+        return this;
+    };
+    
+    elproto.onDragOver = function (f) {
+        f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id);
+    };
+    
+    elproto.undrag = function () {
+        var i = drag.length;
+        while (i--) if (drag[i].el == this) {
+            R.unmousedown(drag[i].start);
+            drag.splice(i++, 1);
+            eve.unbind("drag.*." + this.id);
+        }
+        !drag.length && R.unmousemove(dragMove).unmouseup(dragUp);
+    };
+    
+    paperproto.circle = function (x, y, r) {
+        var out = R._engine.circle(this, x || 0, y || 0, r || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.rect = function (x, y, w, h, r) {
+        var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.ellipse = function (x, y, rx, ry) {
+        var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.path = function (pathString) {
+        pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
+        var out = R._engine.path(R.format[apply](R, arguments), this);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.image = function (src, x, y, w, h) {
+        var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.text = function (x, y, text) {
+        var out = R._engine.text(this, x || 0, y || 0, Str(text));
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.set = function (itemsArray) {
+        !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
+        var out = new Set(itemsArray);
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    paperproto.setStart = function (set) {
+        this.__set__ = set || this.set();
+    };
+    
+    paperproto.setFinish = function (set) {
+        var out = this.__set__;
+        delete this.__set__;
+        return out;
+    };
+    
+    paperproto.setSize = function (width, height) {
+        return R._engine.setSize.call(this, width, height);
+    };
+    
+    paperproto.setViewBox = function (x, y, w, h, fit) {
+        return R._engine.setViewBox.call(this, x, y, w, h, fit);
+    };
+    
+    
+    paperproto.top = paperproto.bottom = null;
+    
+    paperproto.raphael = R;
+    var getOffset = function (elem) {
+        var box = elem.getBoundingClientRect(),
+            doc = elem.ownerDocument,
+            body = doc.body,
+            docElem = doc.documentElement,
+            clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+            top  = box.top  + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
+            left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
+        return {
+            y: top,
+            x: left
+        };
+    };
+    
+    paperproto.getElementByPoint = function (x, y) {
+        var paper = this,
+            svg = paper.canvas,
+            target = g.doc.elementFromPoint(x, y);
+        if (g.win.opera && target.tagName == "svg") {
+            var so = getOffset(svg),
+                sr = svg.createSVGRect();
+            sr.x = x - so.x;
+            sr.y = y - so.y;
+            sr.width = sr.height = 1;
+            var hits = svg.getIntersectionList(sr, null);
+            if (hits.length) {
+                target = hits[hits.length - 1];
+            }
+        }
+        if (!target) {
+            return null;
+        }
+        while (target.parentNode && target != svg.parentNode && !target.raphael) {
+            target = target.parentNode;
+        }
+        target == paper.canvas.parentNode && (target = svg);
+        target = target && target.raphael ? paper.getById(target.raphaelid) : null;
+        return target;
+    };
+    
+    paperproto.getById = function (id) {
+        var bot = this.bottom;
+        while (bot) {
+            if (bot.id == id) {
+                return bot;
+            }
+            bot = bot.next;
+        }
+        return null;
+    };
+    
+    paperproto.forEach = function (callback, thisArg) {
+        var bot = this.bottom;
+        while (bot) {
+            if (callback.call(thisArg, bot) === false) {
+                return this;
+            }
+            bot = bot.next;
+        }
+        return this;
+    };
+    function x_y() {
+        return this.x + S + this.y;
+    }
+    function x_y_w_h() {
+        return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
+    }
+    
+    elproto.getBBox = function (isWithoutTransform) {
+        if (this.removed) {
+            return {};
+        }
+        var _ = this._;
+        if (isWithoutTransform) {
+            if (_.dirty || !_.bboxwt) {
+                this.realPath = getPath[this.type](this);
+                _.bboxwt = pathDimensions(this.realPath);
+                _.bboxwt.toString = x_y_w_h;
+                _.dirty = 0;
+            }
+            return _.bboxwt;
+        }
+        if (_.dirty || _.dirtyT || !_.bbox) {
+            if (_.dirty || !this.realPath) {
+                _.bboxwt = 0;
+                this.realPath = getPath[this.type](this);
+            }
+            _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
+            _.bbox.toString = x_y_w_h;
+            _.dirty = _.dirtyT = 0;
+        }
+        return _.bbox;
+    };
+    
+    elproto.clone = function () {
+        if (this.removed) {
+            return null;
+        }
+        var out = this.paper[this.type]().attr(this.attr());
+        this.__set__ && this.__set__.push(out);
+        return out;
+    };
+    
+    elproto.glow = function (glow) {
+        if (this.type == "text") {
+            return null;
+        }
+        glow = glow || {};
+        var s = {
+            width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
+            fill: glow.fill || false,
+            opacity: glow.opacity || .5,
+            offsetx: glow.offsetx || 0,
+            offsety: glow.offsety || 0,
+            color: glow.color || "#000"
+        },
+            c = s.width / 2,
+            r = this.paper,
+            out = r.set(),
+            path = this.realPath || getPath[this.type](this);
+        path = this.matrix ? mapPath(path, this.matrix) : path;
+        for (var i = 1; i < c + 1; i++) {
+            out.push(r.path(path).attr({
+                stroke: s.color,
+                fill: s.fill ? s.color : "none",
+                "stroke-linejoin": "round",
+                "stroke-linecap": "round",
+                "stroke-width": +(s.width / c * i).toFixed(3),
+                opacity: +(s.opacity / c).toFixed(3)
+            }));
+        }
+        return out.insertBefore(this).translate(s.offsetx, s.offsety);
+    };
+    var curveslengths = {},
+    getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
+        var len = 0,
+            precision = 100,
+            name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(),
+            cache = curveslengths[name],
+            old, dot;
+        !cache && (curveslengths[name] = cache = {data: []});
+        cache.timer && clearTimeout(cache.timer);
+        cache.timer = setTimeout(function () {delete curveslengths[name];}, 2e3);
+        if (length != null && !cache.precision) {
+            var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
+            cache.precision = ~~total * 10;
+            cache.data = [];
+        }
+        precision = cache.precision || precision;
+        for (var i = 0; i < precision + 1; i++) {
+            if (cache.data[i * precision]) {
+                dot = cache.data[i * precision];
+            } else {
+                dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision);
+                cache.data[i * precision] = dot;
+            }
+            i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
+            if (length != null && len >= length) {
+                return dot;
+            }
+            old = dot;
+        }
+        if (length == null) {
+            return len;
+        }
+    },
+    getLengthFactory = function (istotal, subpath) {
+        return function (path, length, onlystart) {
+            path = path2curve(path);
+            var x, y, p, l, sp = "", subpaths = {}, point,
+                len = 0;
+            for (var i = 0, ii = path.length; i < ii; i++) {
+                p = path[i];
+                if (p[0] == "M") {
+                    x = +p[1];
+                    y = +p[2];
+                } else {
+                    l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+                    if (len + l > length) {
+                        if (subpath && !subpaths.start) {
+                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
+                            sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
+                            if (onlystart) {return sp;}
+                            subpaths.start = sp;
+                            sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
+                            len += l;
+                            x = +p[5];
+                            y = +p[6];
+                            continue;
+                        }
+                        if (!istotal && !subpath) {
+                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
+                            return {x: point.x, y: point.y, alpha: point.alpha};
+                        }
+                    }
+                    len += l;
+                    x = +p[5];
+                    y = +p[6];
+                }
+                sp += p.shift() + p;
+            }
+            subpaths.end = sp;
+            point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
+            point.alpha && (point = {x: point.x, y: point.y, alpha: point.alpha});
+            return point;
+        };
+    };
+    var getTotalLength = getLengthFactory(1),
+        getPointAtLength = getLengthFactory(),
+        getSubpathsAtLength = getLengthFactory(0, 1);
+    
+    R.getTotalLength = getTotalLength;
+    
+    R.getPointAtLength = getPointAtLength;
+    
+    R.getSubpath = function (path, from, to) {
+        if (this.getTotalLength(path) - to < 1e-6) {
+            return getSubpathsAtLength(path, from).end;
+        }
+        var a = getSubpathsAtLength(path, to, 1);
+        return from ? getSubpathsAtLength(a, from).end : a;
+    };
+    
+    elproto.getTotalLength = function () {
+        if (this.type != "path") {return;}
+        if (this.node.getTotalLength) {
+            return this.node.getTotalLength();
+        }
+        return getTotalLength(this.attrs.path);
+    };
+    
+    elproto.getPointAtLength = function (length) {
+        if (this.type != "path") {return;}
+        return getPointAtLength(this.attrs.path, length);
+    };
+    
+    elproto.getSubpath = function (from, to) {
+        if (this.type != "path") {return;}
+        return R.getSubpath(this.attrs.path, from, to);
+    };
+    
+    var ef = R.easing_formulas = {
+        linear: function (n) {
+            return n;
+        },
+        "<": function (n) {
+            return pow(n, 1.7);
+        },
+        ">": function (n) {
+            return pow(n, .48);
+        },
+        "<>": function (n) {
+            var q = .48 - n / 1.04,
+                Q = math.sqrt(.1734 + q * q),
+                x = Q - q,
+                X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
+                y = -Q - q,
+                Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
+                t = X + Y + .5;
+            return (1 - t) * 3 * t * t + t * t * t;
+        },
+        backIn: function (n) {
+            var s = 1.70158;
+            return n * n * ((s + 1) * n - s);
+        },
+        backOut: function (n) {
+            n = n - 1;
+            var s = 1.70158;
+            return n * n * ((s + 1) * n + s) + 1;
+        },
+        elastic: function (n) {
+            if (n == !!n) {
+                return n;
+            }
+            return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
+        },
+        bounce: function (n) {
+            var s = 7.5625,
+                p = 2.75,
+                l;
+            if (n < (1 / p)) {
+                l = s * n * n;
+            } else {
+                if (n < (2 / p)) {
+                    n -= (1.5 / p);
+                    l = s * n * n + .75;
+                } else {
+                    if (n < (2.5 / p)) {
+                        n -= (2.25 / p);
+                        l = s * n * n + .9375;
+                    } else {
+                        n -= (2.625 / p);
+                        l = s * n * n + .984375;
+                    }
+                }
+            }
+            return l;
+        }
+    };
+    ef.easeIn = ef["ease-in"] = ef["<"];
+    ef.easeOut = ef["ease-out"] = ef[">"];
+    ef.easeInOut = ef["ease-in-out"] = ef["<>"];
+    ef["back-in"] = ef.backIn;
+    ef["back-out"] = ef.backOut;
+
+    var animationElements = [],
+        requestAnimFrame = window.requestAnimationFrame       ||
+                           window.webkitRequestAnimationFrame ||
+                           window.mozRequestAnimationFrame    ||
+                           window.oRequestAnimationFrame      ||
+                           window.msRequestAnimationFrame     ||
+                           function (callback) {
+                               setTimeout(callback, 16);
+                           },
+        animation = function () {
+            var Now = +new Date,
+                l = 0;
+            for (; l < animationElements.length; l++) {
+                var e = animationElements[l];
+                if (e.el.removed || e.paused) {
+                    continue;
+                }
+                var time = Now - e.start,
+                    ms = e.ms,
+                    easing = e.easing,
+                    from = e.from,
+                    diff = e.diff,
+                    to = e.to,
+                    t = e.t,
+                    that = e.el,
+                    set = {},
+                    now,
+                    init = {},
+                    key;
+                if (e.initstatus) {
+                    time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
+                    e.status = e.initstatus;
+                    delete e.initstatus;
+                    e.stop && animationElements.splice(l--, 1);
+                } else {
+                    e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
+                }
+                if (time < 0) {
+                    continue;
+                }
+                if (time < ms) {
+                    var pos = easing(time / ms);
+                    for (var attr in from) if (from[has](attr)) {
+                        switch (availableAnimAttrs[attr]) {
+                            case nu:
+                                now = +from[attr] + pos * ms * diff[attr];
+                                break;
+                            case "colour":
+                                now = "rgb(" + [
+                                    upto255(round(from[attr].r + pos * ms * diff[attr].r)),
+                                    upto255(round(from[attr].g + pos * ms * diff[attr].g)),
+                                    upto255(round(from[attr].b + pos * ms * diff[attr].b))
+                                ].join(",") + ")";
+                                break;
+                            case "path":
+                                now = [];
+                                for (var i = 0, ii = from[attr].length; i < ii; i++) {
+                                    now[i] = [from[attr][i][0]];
+                                    for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                        now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
+                                    }
+                                    now[i] = now[i].join(S);
+                                }
+                                now = now.join(S);
+                                break;
+                            case "transform":
+                                if (diff[attr].real) {
+                                    now = [];
+                                    for (i = 0, ii = from[attr].length; i < ii; i++) {
+                                        now[i] = [from[attr][i][0]];
+                                        for (j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                            now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
+                                        }
+                                    }
+                                } else {
+                                    var get = function (i) {
+                                        return +from[attr][i] + pos * ms * diff[attr][i];
+                                    };
+                                    // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
+                                    now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
+                                }
+                                break;
+                            case "csv":
+                                if (attr == "clip-rect") {
+                                    now = [];
+                                    i = 4;
+                                    while (i--) {
+                                        now[i] = +from[attr][i] + pos * ms * diff[attr][i];
+                                    }
+                                }
+                                break;
+                            default:
+                                var from2 = [][concat](from[attr]);
+                                now = [];
+                                i = that.paper.customAttributes[attr].length;
+                                while (i--) {
+                                    now[i] = +from2[i] + pos * ms * diff[attr][i];
+                                }
+                                break;
+                        }
+                        set[attr] = now;
+                    }
+                    that.attr(set);
+                    (function (id, that, anim) {
+                        setTimeout(function () {
+                            eve("anim.frame." + id, that, anim);
+                        });
+                    })(that.id, that, e.anim);
+                } else {
+                    (function(f, el, a) {
+                        setTimeout(function() {
+                            eve("anim.frame." + el.id, el, a);
+                            eve("anim.finish." + el.id, el, a);
+                            R.is(f, "function") && f.call(el);
+                        });
+                    })(e.callback, that, e.anim);
+                    that.attr(to);
+                    animationElements.splice(l--, 1);
+                    if (e.repeat > 1 && !e.next) {
+                        for (key in to) if (to[has](key)) {
+                            init[key] = e.totalOrigin[key];
+                        }
+                        e.el.attr(init);
+                        runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
+                    }
+                    if (e.next && !e.stop) {
+                        runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
+                    }
+                }
+            }
+            R.svg && that && that.paper && that.paper.safari();
+            animationElements.length && requestAnimFrame(animation);
+        },
+        upto255 = function (color) {
+            return color > 255 ? 255 : color < 0 ? 0 : color;
+        };
+    
+    elproto.animateWith = function (element, anim, params, ms, easing, callback) {
+        var a = params ? R.animation(params, ms, easing, callback) : anim;
+            status = element.status(anim);
+        return this.animate(a).status(a, status * anim.ms / a.ms);
+    };
+    function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
+        var cx = 3 * p1x,
+            bx = 3 * (p2x - p1x) - cx,
+            ax = 1 - cx - bx,
+            cy = 3 * p1y,
+            by = 3 * (p2y - p1y) - cy,
+            ay = 1 - cy - by;
+        function sampleCurveX(t) {
+            return ((ax * t + bx) * t + cx) * t;
+        }
+        function solve(x, epsilon) {
+            var t = solveCurveX(x, epsilon);
+            return ((ay * t + by) * t + cy) * t;
+        }
+        function solveCurveX(x, epsilon) {
+            var t0, t1, t2, x2, d2, i;
+            for(t2 = x, i = 0; i < 8; i++) {
+                x2 = sampleCurveX(t2) - x;
+                if (abs(x2) < epsilon) {
+                    return t2;
+                }
+                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
+                if (abs(d2) < 1e-6) {
+                    break;
+                }
+                t2 = t2 - x2 / d2;
+            }
+            t0 = 0;
+            t1 = 1;
+            t2 = x;
+            if (t2 < t0) {
+                return t0;
+            }
+            if (t2 > t1) {
+                return t1;
+            }
+            while (t0 < t1) {
+                x2 = sampleCurveX(t2);
+                if (abs(x2 - x) < epsilon) {
+                    return t2;
+                }
+                if (x > x2) {
+                    t0 = t2;
+                } else {
+                    t1 = t2;
+                }
+                t2 = (t1 - t0) / 2 + t0;
+            }
+            return t2;
+        }
+        return solve(t, 1 / (200 * duration));
+    }
+    elproto.onAnimation = function (f) {
+        f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id);
+        return this;
+    };
+    function Animation(anim, ms) {
+        var percents = [],
+            newAnim = {};
+        this.ms = ms;
+        this.times = 1;
+        if (anim) {
+            for (var attr in anim) if (anim[has](attr)) {
+                newAnim[toFloat(attr)] = anim[attr];
+                percents.push(toFloat(attr));
+            }
+            percents.sort(sortByNumber);
+        }
+        this.anim = newAnim;
+        this.top = percents[percents.length - 1];
+        this.percents = percents;
+    }
+    
+    Animation.prototype.delay = function (delay) {
+        var a = new Animation(this.anim, this.ms);
+        a.times = this.times;
+        a.del = +delay || 0;
+        return a;
+    };
+    
+    Animation.prototype.repeat = function (times) { 
+        var a = new Animation(this.anim, this.ms);
+        a.del = this.del;
+        a.times = math.floor(mmax(times, 0)) || 1;
+        return a;
+    };
+    function runAnimation(anim, element, percent, status, totalOrigin, times) {
+        percent = toFloat(percent);
+        var params,
+            isInAnim,
+            isInAnimSet,
+            percents = [],
+            next,
+            prev,
+            timestamp,
+            ms = anim.ms,
+            from = {},
+            to = {},
+            diff = {};
+        if (status) {
+            for (i = 0, ii = animationElements.length; i < ii; i++) {
+                var e = animationElements[i];
+                if (e.el.id == element.id && e.anim == anim) {
+                    if (e.percent != percent) {
+                        animationElements.splice(i, 1);
+                        isInAnimSet = 1;
+                    } else {
+                        isInAnim = e;
+                    }
+                    element.attr(e.totalOrigin);
+                    break;
+                }
+            }
+        } else {
+            status = +to; // NaN
+        }
+        for (var i = 0, ii = anim.percents.length; i < ii; i++) {
+            if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
+                percent = anim.percents[i];
+                prev = anim.percents[i - 1] || 0;
+                ms = ms / anim.top * (percent - prev);
+                next = anim.percents[i + 1];
+                params = anim.anim[percent];
+                break;
+            } else if (status) {
+                element.attr(anim.anim[anim.percents[i]]);
+            }
+        }
+        if (!params) {
+            return;
+        }
+        if (!isInAnim) {
+            for (attr in params) if (params[has](attr)) {
+                if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
+                    from[attr] = element.attr(attr);
+                    (from[attr] == null) && (from[attr] = availableAttrs[attr]);
+                    to[attr] = params[attr];
+                    switch (availableAnimAttrs[attr]) {
+                        case nu:
+                            diff[attr] = (to[attr] - from[attr]) / ms;
+                            break;
+                        case "colour":
+                            from[attr] = R.getRGB(from[attr]);
+                            var toColour = R.getRGB(to[attr]);
+                            diff[attr] = {
+                                r: (toColour.r - from[attr].r) / ms,
+                                g: (toColour.g - from[attr].g) / ms,
+                                b: (toColour.b - from[attr].b) / ms
+                            };
+                            break;
+                        case "path":
+                            var pathes = path2curve(from[attr], to[attr]),
+                                toPath = pathes[1];
+                            from[attr] = pathes[0];
+                            diff[attr] = [];
+                            for (i = 0, ii = from[attr].length; i < ii; i++) {
+                                diff[attr][i] = [0];
+                                for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                    diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
+                                }
+                            }
+                            break;
+                        case "transform":
+                            var _ = element._,
+                                eq = equaliseTransform(_[attr], to[attr]);
+                            if (eq) {
+                                from[attr] = eq.from;
+                                to[attr] = eq.to;
+                                diff[attr] = [];
+                                diff[attr].real = true;
+                                for (i = 0, ii = from[attr].length; i < ii; i++) {
+                                    diff[attr][i] = [from[attr][i][0]];
+                                    for (j = 1, jj = from[attr][i].length; j < jj; j++) {
+                                        diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
+                                    }
+                                }
+                            } else {
+                                var m = (element.matrix || new Matrix),
+                                    to2 = {
+                                        _: {transform: _.transform},
+                                        getBBox: function () {
+                                            return element.getBBox(1);
+                                        }
+                                    };
+                                from[attr] = [
+                                    m.a,
+                                    m.b,
+                                    m.c,
+                                    m.d,
+                                    m.e,
+                                    m.f
+                                ];
+                                extractTransform(to2, to[attr]);
+                                to[attr] = to2._.transform;
+                                diff[attr] = [
+                                    (to2.matrix.a - m.a) / ms,
+                                    (to2.matrix.b - m.b) / ms,
+                                    (to2.matrix.c - m.c) / ms,
+                                    (to2.matrix.d - m.d) / ms,
+                                    (to2.matrix.e - m.e) / ms,
+                                    (to2.matrix.e - m.f) / ms
+                                ];
+                                // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
+                                // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
+                                // extractTransform(to2, to[attr]);
+                                // diff[attr] = [
+                                //     (to2._.sx - _.sx) / ms,
+                                //     (to2._.sy - _.sy) / ms,
+                                //     (to2._.deg - _.deg) / ms,
+                                //     (to2._.dx - _.dx) / ms,
+                                //     (to2._.dy - _.dy) / ms
+                                // ];
+                            }
+                            break;
+                        case "csv":
+                            var values = Str(params[attr])[split](separator),
+                                from2 = Str(from[attr])[split](separator);
+                            if (attr == "clip-rect") {
+                                from[attr] = from2;
+                                diff[attr] = [];
+                                i = from2.length;
+                                while (i--) {
+                                    diff[attr][i] = (values[i] - from[attr][i]) / ms;
+                                }
+                            }
+                            to[attr] = values;
+                            break;
+                        default:
+                            values = [][concat](params[attr]);
+                            from2 = [][concat](from[attr]);
+                            diff[attr] = [];
+                            i = element.paper.customAttributes[attr].length;
+                            while (i--) {
+                                diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
+                            }
+                            break;
+                    }
+                }
+            }
+            var easing = params.easing,
+                easyeasy = R.easing_formulas[easing];
+            if (!easyeasy) {
+                easyeasy = Str(easing).match(bezierrg);
+                if (easyeasy && easyeasy.length == 5) {
+                    var curve = easyeasy;
+                    easyeasy = function (t) {
+                        return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
+                    };
+                } else {
+                    easyeasy = pipe;
+                }
+            }
+            timestamp = params.start || anim.start || +new Date;
+            e = {
+                anim: anim,
+                percent: percent,
+                timestamp: timestamp,
+                start: timestamp + (anim.del || 0),
+                status: 0,
+                initstatus: status || 0,
+                stop: false,
+                ms: ms,
+                easing: easyeasy,
+                from: from,
+                diff: diff,
+                to: to,
+                el: element,
+                callback: params.callback,
+                prev: prev,
+                next: next,
+                repeat: times || anim.times,
+                origin: element.attr(),
+                totalOrigin: totalOrigin
+            };
+            animationElements.push(e);
+            if (status && !isInAnim && !isInAnimSet) {
+                e.stop = true;
+                e.start = new Date - ms * status;
+                if (animationElements.length == 1) {
+                    return animation();
+                }
+            }
+            if (isInAnimSet) {
+                e.start = new Date - e.ms * status;
+            }
+            animationElements.length == 1 && requestAnimFrame(animation);
+        } else {
+            isInAnim.initstatus = status;
+            isInAnim.start = new Date - isInAnim.ms * status;
+        }
+        eve("anim.start." + element.id, element, anim);
+    }
+    
+    R.animation = function (params, ms, easing, callback) {
+        if (params instanceof Animation) {
+            return params;
+        }
+        if (R.is(easing, "function") || !easing) {
+            callback = callback || easing || null;
+            easing = null;
+        }
+        params = Object(params);
+        ms = +ms || 0;
+        var p = {},
+            json,
+            attr;
+        for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
+            json = true;
+            p[attr] = params[attr];
+        }
+        if (!json) {
+            return new Animation(params, ms);
+        } else {
+            easing && (p.easing = easing);
+            callback && (p.callback = callback);
+            return new Animation({100: p}, ms);
+        }
+    };
+    
+    elproto.animate = function (params, ms, easing, callback) {
+        var element = this;
+        if (element.removed) {
+            callback && callback.call(element);
+            return element;
+        }
+        var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
+        runAnimation(anim, element, anim.percents[0], null, element.attr());
+        return element;
+    };
+    
+    elproto.setTime = function (anim, value) {
+        if (anim && value != null) {
+            this.status(anim, mmin(value, anim.ms) / anim.ms);
+        }
+        return this;
+    };
+    
+    elproto.status = function (anim, value) {
+        var out = [],
+            i = 0,
+            len,
+            e;
+        if (value != null) {
+            runAnimation(anim, this, -1, mmin(value, 1));
+            return this;
+        } else {
+            len = animationElements.length;
+            for (; i < len; i++) {
+                e = animationElements[i];
+                if (e.el.id == this.id && (!anim || e.anim == anim)) {
+                    if (anim) {
+                        return e.status;
+                    }
+                    out.push({
+                        anim: e.anim,
+                        status: e.status
+                    });
+                }
+            }
+            if (anim) {
+                return 0;
+            }
+            return out;
+        }
+    };
+    
+    elproto.pause = function (anim) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
+            if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) {
+                animationElements[i].paused = true;
+            }
+        }
+        return this;
+    };
+    
+    elproto.resume = function (anim) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
+            var e = animationElements[i];
+            if (eve("anim.resume." + this.id, this, e.anim) !== false) {
+                delete e.paused;
+                this.status(e.anim, e.status);
+            }
+        }
+        return this;
+    };
+    
+    elproto.stop = function (anim) {
+        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
+            if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) {
+                animationElements.splice(i--, 1);
+            }
+        }
+        return this;
+    };
+    elproto.toString = function () {
+        return "Rapha\xebl\u2019s object";
+    };
+
+    // Set
+    var Set = function (items) {
+        this.items = [];
+        this.length = 0;
+        this.type = "set";
+        if (items) {
+            for (var i = 0, ii = items.length; i < ii; i++) {
+                if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
+                    this[this.items.length] = this.items[this.items.length] = items[i];
+                    this.length++;
+                }
+            }
+        }
+    },
+    setproto = Set.prototype;
+    
+    setproto.push = function () {
+        var item,
+            len;
+        for (var i = 0, ii = arguments.length; i < ii; i++) {
+            item = arguments[i];
+            if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
+                len = this.items.length;
+                this[len] = this.items[len] = item;
+                this.length++;
+            }
+        }
+        return this;
+    };
+    
+    setproto.pop = function () {
+        this.length && delete this[this.length--];
+        return this.items.pop();
+    };
+    
+    setproto.forEach = function (callback, thisArg) {
+        for (var i = 0, ii = this.items.length; i < ii; i++) {
+            if (callback.call(thisArg, this.items[i], i) === false) {
+                return this;
+            }
+        }
+        return this;
+    };
+    for (var method in elproto) if (elproto[has](method)) {
+        setproto[method] = (function (methodname) {
+            return function () {
+                var arg = arguments;
+                return this.forEach(function (el) {
+                    el[methodname][apply](el, arg);
+                });
+            };
+        })(method);
+    }
+    setproto.attr = function (name, value) {
+        if (name && R.is(name, array) && R.is(name[0], "object")) {
+            for (var j = 0, jj = name.length; j < jj; j++) {
+                this.items[j].attr(name[j]);
+            }
+        } else {
+            for (var i = 0, ii = this.items.length; i < ii; i++) {
+                this.items[i].attr(name, value);
+            }
+        }
+        return this;
+    };
+    
+    setproto.clear = function () {
+        while (this.length) {
+            this.pop();
+        }
+    };
+    
+    setproto.splice = function (index, count, insertion) {
+        index = index < 0 ? mmax(this.length + index, 0) : index;
+        count = mmax(0, mmin(this.length - index, count));
+        var tail = [],
+            todel = [],
+            args = [],
+            i;
+        for (i = 2; i < arguments.length; i++) {
+            args.push(arguments[i]);
+        }
+        for (i = 0; i < count; i++) {
+            todel.push(this[index + i]);
+        }
+        for (; i < this.length - index; i++) {
+            tail.push(this[index + i]);
+        }
+        var arglen = args.length;
+        for (i = 0; i < arglen + tail.length; i++) {
+            this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
+        }
+        i = this.items.length = this.length -= count - arglen;
+        while (this[i]) {
+            delete this[i++];
+        }
+        return new Set(todel);
+    };
+    
+    setproto.exclude = function (el) {
+        for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) {
+            this.splice(i, 1);
+            return true;
+        }
+    };
+    setproto.animate = function (params, ms, easing, callback) {
+        (R.is(easing, "function") || !easing) && (callback = easing || null);
+        var len = this.items.length,
+            i = len,
+            item,
+            set = this,
+            collector;
+        if (!len) {
+            return this;
+        }
+        callback && (collector = function () {
+            !--len && callback.call(set);
+        });
+        easing = R.is(easing, string) ? easing : collector;
+        var anim = R.animation(params, ms, easing, collector);
+        item = this.items[--i].animate(anim);
+        while (i--) {
+            this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim);
+        }
+        return this;
+    };
+    setproto.insertAfter = function (el) {
+        var i = this.items.length;
+        while (i--) {
+            this.items[i].insertAfter(el);
+        }
+        return this;
+    };
+    setproto.getBBox = function () {
+        var x = [],
+            y = [],
+            w = [],
+            h = [];
+        for (var i = this.items.length; i--;) if (!this.items[i].removed) {
+            var box = this.items[i].getBBox();
+            x.push(box.x);
+            y.push(box.y);
+            w.push(box.x + box.width);
+            h.push(box.y + box.height);
+        }
+        x = mmin[apply](0, x);
+        y = mmin[apply](0, y);
+        return {
+            x: x,
+            y: y,
+            width: mmax[apply](0, w) - x,
+            height: mmax[apply](0, h) - y
+        };
+    };
+    setproto.clone = function (s) {
+        s = new Set;
+        for (var i = 0, ii = this.items.length; i < ii; i++) {
+            s.push(this.items[i].clone());
+        }
+        return s;
+    };
+    setproto.toString = function () {
+        return "Rapha\xebl\u2018s set";
+    };
+
+    
+    R.registerFont = function (font) {
+        if (!font.face) {
+            return font;
+        }
+        this.fonts = this.fonts || {};
+        var fontcopy = {
+                w: font.w,
+                face: {},
+                glyphs: {}
+            },
+            family = font.face["font-family"];
+        for (var prop in font.face) if (font.face[has](prop)) {
+            fontcopy.face[prop] = font.face[prop];
+        }
+        if (this.fonts[family]) {
+            this.fonts[family].push(fontcopy);
+        } else {
+            this.fonts[family] = [fontcopy];
+        }
+        if (!font.svg) {
+            fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
+            for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
+                var path = font.glyphs[glyph];
+                fontcopy.glyphs[glyph] = {
+                    w: path.w,
+                    k: {},
+                    d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
+                            return {l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
+                        }) + "z"
+                };
+                if (path.k) {
+                    for (var k in path.k) if (path[has](k)) {
+                        fontcopy.glyphs[glyph].k[k] = path.k[k];
+                    }
+                }
+            }
+        }
+        return font;
+    };
+    
+    paperproto.getFont = function (family, weight, style, stretch) {
+        stretch = stretch || "normal";
+        style = style || "normal";
+        weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
+        if (!R.fonts) {
+            return;
+        }
+        var font = R.fonts[family];
+        if (!font) {
+            var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
+            for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
+                if (name.test(fontName)) {
+                    font = R.fonts[fontName];
+                    break;
+                }
+            }
+        }
+        var thefont;
+        if (font) {
+            for (var i = 0, ii = font.length; i < ii; i++) {
+                thefont = font[i];
+                if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
+                    break;
+                }
+            }
+        }
+        return thefont;
+    };
+    
+    paperproto.print = function (x, y, string, font, size, origin, letter_spacing) {
+        origin = origin || "middle"; // baseline|middle
+        letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
+        var out = this.set(),
+            letters = Str(string)[split](E),
+            shift = 0,
+            path = E,
+            scale;
+        R.is(font, string) && (font = this.getFont(font));
+        if (font) {
+            scale = (size || 16) / font.face["units-per-em"];
+            var bb = font.face.bbox[split](separator),
+                top = +bb[0],
+                height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
+            for (var i = 0, ii = letters.length; i < ii; i++) {
+                var prev = i && font.glyphs[letters[i - 1]] || {},
+                    curr = font.glyphs[letters[i]];
+                shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
+                curr && curr.d && out.push(this.path(curr.d).attr({
+                    fill: "#000",
+                    stroke: "none",
+                    transform: [["t", shift * scale, 0]]
+                }));
+            }
+            out.transform(["...s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
+        }
+        return out;
+    };
+
+    
+    R.format = function (token, params) {
+        var args = R.is(params, array) ? [0][concat](params) : arguments;
+        token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
+            return args[++i] == null ? E : args[i];
+        }));
+        return token || E;
+    };
+    
+    R.fullfill = (function () {
+        var tokenRegex = /\{([^\}]+)\}/g,
+            objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
+            replacer = function (all, key, obj) {
+                var res = obj;
+                key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
+                    name = name || quotedName;
+                    if (res) {
+                        if (name in res) {
+                            res = res[name];
+                        }
+                        typeof res == "function" && isFunc && (res = res());
+                    }
+                });
+                res = (res == null || res == obj ? all : res) + "";
+                return res;
+            };
+        return function (str, obj) {
+            return String(str).replace(tokenRegex, function (all, key) {
+                return replacer(all, key, obj);
+            });
+        };
+    })();
+    
+    R.ninja = function () {
+        oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
+        return R;
+    };
+    
+    R.st = setproto;
+    // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
+    (function (doc, loaded, f) {
+        if (doc.readyState == null && doc.addEventListener){
+            doc.addEventListener(loaded, f = function () {
+                doc.removeEventListener(loaded, f, false);
+                doc.readyState = "complete";
+            }, false);
+            doc.readyState = "loading";
+        }
+        function isLoaded() {
+            (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("DOMload");
+        }
+        isLoaded();
+    })(document, "DOMContentLoaded");
+
+    oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);
+    
+    eve.on("DOMload", function () {
+        loaded = true;
+    });
+})();
+
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ Raphaël 2 - JavaScript Vector Library                               │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ SVG Module                                                          │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+window.Raphael.svg && function (R) {
+    var has = "hasOwnProperty",
+        Str = String,
+        toFloat = parseFloat,
+        toInt = parseInt,
+        math = Math,
+        mmax = math.max,
+        abs = math.abs,
+        pow = math.pow,
+        separator = /[, ]+/,
+        eve = R.eve,
+        E = "",
+        S = " ";
+    var xlink = "http://www.w3.org/1999/xlink",
+        markers = {
+            block: "M5,0 0,2.5 5,5z",
+            classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
+            diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
+            open: "M6,1 1,3.5 6,6",
+            oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
+        },
+        markerCounter = {};
+    R.toString = function () {
+        return  "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
+    };
+    var $ = function (el, attr) {
+        if (attr) {
+            if (typeof el == "string") {
+                el = $(el);
+            }
+            for (var key in attr) if (attr[has](key)) {
+                if (key.substring(0, 6) == "xlink:") {
+                    el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
+                } else {
+                    el.setAttribute(key, Str(attr[key]));
+                }
+            }
+        } else {
+            el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
+            el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
+        }
+        return el;
+    },
+    gradients = {},
+    rgGrad = /^url\(#(.*)\)$/,
+    removeGradientFill = function (node, paper) {
+        var oid = node.getAttribute("fill");
+        oid = oid && oid.match(rgGrad);
+        if (oid && !--gradients[oid[1]]) {
+            delete gradients[oid[1]];
+            paper.defs.removeChild(R._g.doc.getElementById(oid[1]));
+        }
+    },
+    addGradientFill = function (element, gradient) {
+        var type = "linear",
+            id = element.id + gradient,
+            fx = .5, fy = .5,
+            o = element.node,
+            SVG = element.paper,
+            s = o.style,
+            el = R._g.doc.getElementById(id);
+        if (!el) {
+            gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
+                type = "radial";
+                if (_fx && _fy) {
+                    fx = toFloat(_fx);
+                    fy = toFloat(_fy);
+                    var dir = ((fy > .5) * 2 - 1);
+                    pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
+                        (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
+                        fy != .5 &&
+                        (fy = fy.toFixed(5) - 1e-5 * dir);
+                }
+                return E;
+            });
+            gradient = gradient.split(/\s*\-\s*/);
+            if (type == "linear") {
+                var angle = gradient.shift();
+                angle = -toFloat(angle);
+                if (isNaN(angle)) {
+                    return null;
+                }
+                var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
+                    max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
+                vector[2] *= max;
+                vector[3] *= max;
+                if (vector[2] < 0) {
+                    vector[0] = -vector[2];
+                    vector[2] = 0;
+                }
+                if (vector[3] < 0) {
+                    vector[1] = -vector[3];
+                    vector[3] = 0;
+                }
+            }
+            var dots = R._parseDots(gradient);
+            if (!dots) {
+                return null;
+            }
+            if (element.gradient) {
+                SVG.defs.removeChild(element.gradient);
+                delete element.gradient;
+            }
+
+            id = id.replace(/[\(\)\s,\xb0#]/g, "-");
+            el = $(type + "Gradient", {id: id});
+            element.gradient = el;
+            $(el, type == "radial" ? {
+                fx: fx,
+                fy: fy
+            } : {
+                x1: vector[0],
+                y1: vector[1],
+                x2: vector[2],
+                y2: vector[3],
+                gradientTransform: element.matrix.invert()
+            });
+            SVG.defs.appendChild(el);
+            for (var i = 0, ii = dots.length; i < ii; i++) {
+                el.appendChild($("stop", {
+                    offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
+                    "stop-color": dots[i].color || "#fff"
+                }));
+            }
+        }
+        $(o, {
+            fill: "url(#" + id + ")",
+            opacity: 1,
+            "fill-opacity": 1
+        });
+        s.fill = E;
+        s.opacity = 1;
+        s.fillOpacity = 1;
+        return 1;
+    },
+    updatePosition = function (o) {
+        var bbox = o.getBBox(1);
+        $(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
+    },
+    addArrow = function (o, value, isEnd) {
+        if (o.type == "path") {
+            var values = Str(value).toLowerCase().split("-"),
+                p = o.paper,
+                se = isEnd ? "end" : "start",
+                node = o.node,
+                attrs = o.attrs,
+                stroke = attrs["stroke-width"],
+                i = values.length,
+                type = "classic",
+                from,
+                to,
+                dx,
+                refX,
+                attr,
+                w = 3,
+                h = 3,
+                t = 5;
+            while (i--) {
+                switch (values[i]) {
+                    case "block":
+                    case "classic":
+                    case "oval":
+                    case "diamond":
+                    case "open":
+                    case "none":
+                        type = values[i];
+                        break;
+                    case "wide": h = 5; break;
+                    case "narrow": h = 2; break;
+                    case "long": w = 5; break;
+                    case "short": w = 2; break;
+                }
+            }
+            if (type == "open") {
+                w += 2;
+                h += 2;
+                t += 2;
+                dx = 1;
+                refX = isEnd ? 4 : 1;
+                attr = {
+                    fill: "none",
+                    stroke: attrs.stroke
+                };
+            } else {
+                refX = dx = w / 2;
+                attr = {
+                    fill: attrs.stroke,
+                    stroke: "none"
+                };
+            }
+            if (o._.arrows) {
+                if (isEnd) {
+                    o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
+                    o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
+                } else {
+                    o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
+                    o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
+                }
+            } else {
+                o._.arrows = {};
+            }
+            if (type != "none") {
+                var pathId = "raphael-marker-" + type,
+                    markerId = "raphael-marker-" + se + type + w + h;
+                if (!R._g.doc.getElementById(pathId)) {
+                    p.defs.appendChild($($("path"), {
+                        "stroke-linecap": "round",
+                        d: markers[type],
+                        id: pathId
+                    }));
+                    markerCounter[pathId] = 1;
+                } else {
+                    markerCounter[pathId]++;
+                }
+                var marker = R._g.doc.getElementById(markerId),
+                    use;
+                if (!marker) {
+                    marker = $($("marker"), {
+                        id: markerId,
+                        markerHeight: h,
+                        markerWidth: w,
+                        orient: "auto",
+                        refX: refX,
+                        refY: h / 2
+                    });
+                    use = $($("use"), {
+                        "xlink:href": "#" + pathId,
+                        transform: (isEnd ? " rotate(180 " + w / 2 + " " + h / 2 + ") " : S) + "scale(" + w / t + "," + h / t + ")",
+                        "stroke-width": 1 / ((w / t + h / t) / 2)
+                    });
+                    marker.appendChild(use);
+                    p.defs.appendChild(marker);
+                    markerCounter[markerId] = 1;
+                } else {
+                    markerCounter[markerId]++;
+                    use = marker.getElementsByTagName("use")[0];
+                }
+                $(use, attr);
+                var delta = dx * (type != "diamond" && type != "oval");
+                if (isEnd) {
+                    from = o._.arrows.startdx * stroke || 0;
+                    to = R.getTotalLength(attrs.path) - delta * stroke;
+                } else {
+                    from = delta * stroke;
+                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
+                }
+                attr = {};
+                attr["marker-" + se] = "url(#" + markerId + ")";
+                if (to || from) {
+                    attr.d = Raphael.getSubpath(attrs.path, from, to);
+                }
+                $(node, attr);
+                o._.arrows[se + "Path"] = pathId;
+                o._.arrows[se + "Marker"] = markerId;
+                o._.arrows[se + "dx"] = delta;
+                o._.arrows[se + "Type"] = type;
+                o._.arrows[se + "String"] = value;
+            } else {
+                if (isEnd) {
+                    from = o._.arrows.startdx * stroke || 0;
+                    to = R.getTotalLength(attrs.path) - from;
+                } else {
+                    from = 0;
+                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
+                }
+                o._.arrows[se + "Path"] && $(node, {d: Raphael.getSubpath(attrs.path, from, to)});
+                delete o._.arrows[se + "Path"];
+                delete o._.arrows[se + "Marker"];
+                delete o._.arrows[se + "dx"];
+                delete o._.arrows[se + "Type"];
+                delete o._.arrows[se + "String"];
+            }
+            for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
+                var item = R._g.doc.getElementById(attr);
+                item && item.parentNode.removeChild(item);
+            }
+        }
+    },
+    dasharray = {
+        "": [0],
+        "none": [0],
+        "-": [3, 1],
+        ".": [1, 1],
+        "-.": [3, 1, 1, 1],
+        "-..": [3, 1, 1, 1, 1, 1],
+        ". ": [1, 3],
+        "- ": [4, 3],
+        "--": [8, 3],
+        "- .": [4, 3, 1, 3],
+        "--.": [8, 3, 1, 3],
+        "--..": [8, 3, 1, 3, 1, 3]
+    },
+    addDashes = function (o, value, params) {
+        value = dasharray[Str(value).toLowerCase()];
+        if (value) {
+            var width = o.attrs["stroke-width"] || "1",
+                butt = {round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
+                dashes = [],
+                i = value.length;
+            while (i--) {
+                dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
+            }
+            $(o.node, {"stroke-dasharray": dashes.join(",")});
+        }
+    },
+    setFillAndStroke = function (o, params) {
+        var node = o.node,
+            attrs = o.attrs,
+            vis = node.style.visibility;
+        node.style.visibility = "hidden";
+        for (var att in params) {
+            if (params[has](att)) {
+                if (!R._availableAttrs[has](att)) {
+                    continue;
+                }
+                var value = params[att];
+                attrs[att] = value;
+                switch (att) {
+                    case "blur":
+                        o.blur(value);
+                        break;
+                    case "href":
+                    case "title":
+                    case "target":
+                        var pn = node.parentNode;
+                        if (pn.tagName.toLowerCase() != "a") {
+                            var hl = $("a");
+                            pn.insertBefore(hl, node);
+                            hl.appendChild(node);
+                            pn = hl;
+                        }
+                        if (att == "target" && value == "blank") {
+                            pn.setAttributeNS(xlink, "show", "new");
+                        } else {
+                            pn.setAttributeNS(xlink, att, value);
+                        }
+                        break;
+                    case "cursor":
+                        node.style.cursor = value;
+                        break;
+                    case "transform":
+                        o.transform(value);
+                        break;
+                    case "arrow-start":
+                        addArrow(o, value);
+                        break;
+                    case "arrow-end":
+                        addArrow(o, value, 1);
+                        break;
+                    case "clip-rect":
+                        var rect = Str(value).split(separator);
+                        if (rect.length == 4) {
+                            o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
+                            var el = $("clipPath"),
+                                rc = $("rect");
+                            el.id = R.createUUID();
+                            $(rc, {
+                                x: rect[0],
+                                y: rect[1],
+                                width: rect[2],
+                                height: rect[3]
+                            });
+                            el.appendChild(rc);
+                            o.paper.defs.appendChild(el);
+                            $(node, {"clip-path": "url(#" + el.id + ")"});
+                            o.clip = rc;
+                        }
+                        if (!value) {
+                            var clip = R._g.doc.getElementById(node.getAttribute("clip-path").replace(/(^url\(#|\)$)/g, E));
+                            clip && clip.parentNode.removeChild(clip);
+                            $(node, {"clip-path": E});
+                            delete o.clip;
+                        }
+                    break;
+                    case "path":
+                        if (o.type == "path") {
+                            $(node, {d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0"});
+                            o._.dirty = 1;
+                            if (o._.arrows) {
+                                "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
+                                "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
+                            }
+                        }
+                        break;
+                    case "width":
+                        node.setAttribute(att, value);
+                        o._.dirty = 1;
+                        if (attrs.fx) {
+                            att = "x";
+                            value = attrs.x;
+                        } else {
+                            break;
+                        }
+                    case "x":
+                        if (attrs.fx) {
+                            value = -attrs.x - (attrs.width || 0);
+                        }
+                    case "rx":
+                        if (att == "rx" && o.type == "rect") {
+                            break;
+                        }
+                    case "cx":
+                        node.setAttribute(att, value);
+                        o.pattern && updatePosition(o);
+                        o._.dirty = 1;
+                        break;
+                    case "height":
+                        node.setAttribute(att, value);
+                        o._.dirty = 1;
+                        if (attrs.fy) {
+                            att = "y";
+                            value = attrs.y;
+                        } else {
+                            break;
+                        }
+                    case "y":
+                        if (attrs.fy) {
+                            value = -attrs.y - (attrs.height || 0);
+                        }
+                    case "ry":
+                        if (att == "ry" && o.type == "rect") {
+                            break;
+                        }
+                    case "cy":
+                        node.setAttribute(att, value);
+                        o.pattern && updatePosition(o);
+                        o._.dirty = 1;
+                        break;
+                    case "r":
+                        if (o.type == "rect") {
+                            $(node, {rx: value, ry: value});
+                        } else {
+                            node.setAttribute(att, value);
+                        }
+                        o._.dirty = 1;
+                        break;
+                    case "src":
+                        if (o.type == "image") {
+                            node.setAttributeNS(xlink, "href", value);
+                        }
+                        break;
+                    case "stroke-width":
+                        if (o._.sx != 1 || o._.sy != 1) {
+                            value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
+                        }
+                        if (o.paper._vbSize) {
+                            value *= o.paper._vbSize;
+                        }
+                        node.setAttribute(att, value);
+                        if (attrs["stroke-dasharray"]) {
+                            addDashes(o, attrs["stroke-dasharray"], params);
+                        }
+                        if (o._.arrows) {
+                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
+                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
+                        }
+                        break;
+                    case "stroke-dasharray":
+                        addDashes(o, value, params);
+                        break;
+                    case "fill":
+                        var isURL = Str(value).match(R._ISURL);
+                        if (isURL) {
+                            el = $("pattern");
+                            var ig = $("image");
+                            el.id = R.createUUID();
+                            $(el, {x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1});
+                            $(ig, {x: 0, y: 0, "xlink:href": isURL[1]});
+                            el.appendChild(ig);
+
+                            (function (el) {
+                                R._preload(isURL[1], function () {
+                                    var w = this.offsetWidth,
+                                        h = this.offsetHeight;
+                                    $(el, {width: w, height: h});
+                                    $(ig, {width: w, height: h});
+                                    o.paper.safari();
+                                });
+                            })(el);
+                            o.paper.defs.appendChild(el);
+                            node.style.fill = "url(#" + el.id + ")";
+                            $(node, {fill: "url(#" + el.id + ")"});
+                            o.pattern = el;
+                            o.pattern && updatePosition(o);
+                            break;
+                        }
+                        var clr = R.getRGB(value);
+                        if (!clr.error) {
+                            delete params.gradient;
+                            delete attrs.gradient;
+                            !R.is(attrs.opacity, "undefined") &&
+                                R.is(params.opacity, "undefined") &&
+                                $(node, {opacity: attrs.opacity});
+                            !R.is(attrs["fill-opacity"], "undefined") &&
+                                R.is(params["fill-opacity"], "undefined") &&
+                                $(node, {"fill-opacity": attrs["fill-opacity"]});
+                        } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
+                            if ("opacity" in attrs || "fill-opacity" in attrs) {
+                                var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
+                                if (gradient) {
+                                    var stops = gradient.getElementsByTagName("stop");
+                                    $(stops[stops.length - 1], {"stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1)});
+                                }
+                            }
+                            attrs.gradient = value;
+                            attrs.fill = "none";
+                            break;
+                        }
+                        clr[has]("opacity") && $(node, {"fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
+                    case "stroke":
+                        clr = R.getRGB(value);
+                        node.setAttribute(att, clr.hex);
+                        att == "stroke" && clr[has]("opacity") && $(node, {"stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity});
+                        if (att == "stroke" && o._.arrows) {
+                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
+                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
+                        }
+                        break;
+                    case "gradient":
+                        (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
+                        break;
+                    case "opacity":
+                        if (attrs.gradient && !attrs[has]("stroke-opacity")) {
+                            $(node, {"stroke-opacity": value > 1 ? value / 100 : value});
+                        }
+                        // fall
+                    case "fill-opacity":
+                        if (attrs.gradient) {
+                            gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
+                            if (gradient) {
+                                stops = gradient.getElementsByTagName("stop");
+                                $(stops[stops.length - 1], {"stop-opacity": value});
+                            }
+                            break;
+                        }
+                    default:
+                        att == "font-size" && (value = toInt(value, 10) + "px");
+                        var cssrule = att.replace(/(\-.)/g, function (w) {
+                            return w.substring(1).toUpperCase();
+                        });
+                        node.style[cssrule] = value;
+                        o._.dirty = 1;
+                        node.setAttribute(att, value);
+                        break;
+                }
+            }
+        }
+
+        tuneText(o, params);
+        node.style.visibility = vis;
+    },
+    leading = 1.2,
+    tuneText = function (el, params) {
+        if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
+            return;
+        }
+        var a = el.attrs,
+            node = el.node,
+            fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;
+
+        if (params[has]("text")) {
+            a.text = params.text;
+            while (node.firstChild) {
+                node.removeChild(node.firstChild);
+            }
+            var texts = Str(params.text).split("\n"),
+                tspans = [],
+                tspan;
+            for (var i = 0, ii = texts.length; i < ii; i++) {
+                tspan = $("tspan");
+                i && $(tspan, {dy: fontSize * leading, x: a.x});
+                tspan.appendChild(R._g.doc.createTextNode(texts[i]));
+                node.appendChild(tspan);
+                tspans[i] = tspan;
+            }
+        } else {
+            tspans = node.getElementsByTagName("tspan");
+            for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
+                $(tspans[i], {dy: fontSize * leading, x: a.x});
+            } else {
+                $(tspans[0], {dy: 0});
+            }
+        }
+        $(node, {x: a.x, y: a.y});
+        el._.dirty = 1;
+        var bb = el._getBBox(),
+            dif = a.y - (bb.y + bb.height / 2);
+        dif && R.is(dif, "finite") && $(tspans[0], {dy: dif});
+    },
+    Element = function (node, svg) {
+        var X = 0,
+            Y = 0;
+        
+        this[0] = this.node = node;
+        
+        node.raphael = true;
+        
+        this.id = R._oid++;
+        node.raphaelid = this.id;
+        this.matrix = R.matrix();
+        this.realPath = null;
+        
+        this.paper = svg;
+        this.attrs = this.attrs || {};
+        this._ = {
+            transform: [],
+            sx: 1,
+            sy: 1,
+            deg: 0,
+            dx: 0,
+            dy: 0,
+            dirty: 1
+        };
+        !svg.bottom && (svg.bottom = this);
+        
+        this.prev = svg.top;
+        svg.top && (svg.top.next = this);
+        svg.top = this;
+        
+        this.next = null;
+    },
+    elproto = R.el;
+
+    Element.prototype = elproto;
+    elproto.constructor = Element;
+
+    R._engine.path = function (pathString, SVG) {
+        var el = $("path");
+        SVG.canvas && SVG.canvas.appendChild(el);
+        var p = new Element(el, SVG);
+        p.type = "path";
+        setFillAndStroke(p, {
+            fill: "none",
+            stroke: "#000",
+            path: pathString
+        });
+        return p;
+    };
+    
+    elproto.rotate = function (deg, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        deg = Str(deg).split(separator);
+        if (deg.length - 1) {
+            cx = toFloat(deg[1]);
+            cy = toFloat(deg[2]);
+        }
+        deg = toFloat(deg[0]);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+            cx = bbox.x + bbox.width / 2;
+            cy = bbox.y + bbox.height / 2;
+        }
+        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
+        return this;
+    };
+    
+    elproto.scale = function (sx, sy, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        sx = Str(sx).split(separator);
+        if (sx.length - 1) {
+            sy = toFloat(sx[1]);
+            cx = toFloat(sx[2]);
+            cy = toFloat(sx[3]);
+        }
+        sx = toFloat(sx[0]);
+        (sy == null) && (sy = sx);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+        }
+        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
+        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
+        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
+        return this;
+    };
+    
+    elproto.translate = function (dx, dy) {
+        if (this.removed) {
+            return this;
+        }
+        dx = Str(dx).split(separator);
+        if (dx.length - 1) {
+            dy = toFloat(dx[1]);
+        }
+        dx = toFloat(dx[0]) || 0;
+        dy = +dy || 0;
+        this.transform(this._.transform.concat([["t", dx, dy]]));
+        return this;
+    };
+    
+    elproto.transform = function (tstr) {
+        var _ = this._;
+        if (tstr == null) {
+            return _.transform;
+        }
+        R._extractTransform(this, tstr);
+
+        this.clip && $(this.clip, {transform: this.matrix.invert()});
+        this.pattern && updatePosition(this);
+        this.node && $(this.node, {transform: this.matrix});
+    
+        if (_.sx != 1 || _.sy != 1) {
+            var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
+            this.attr({"stroke-width": sw});
+        }
+
+        return this;
+    };
+    
+    elproto.hide = function () {
+        !this.removed && this.paper.safari(this.node.style.display = "none");
+        return this;
+    };
+    
+    elproto.show = function () {
+        !this.removed && this.paper.safari(this.node.style.display = "");
+        return this;
+    };
+    
+    elproto.remove = function () {
+        if (this.removed) {
+            return;
+        }
+        this.paper.__set__ && this.paper.__set__.exclude(this);
+        eve.unbind("*.*." + this.id);
+        R._tear(this, this.paper);
+        this.node.parentNode.removeChild(this.node);
+        for (var i in this) {
+            delete this[i];
+        }
+        this.removed = true;
+    };
+    elproto._getBBox = function () {
+        if (this.node.style.display == "none") {
+            this.show();
+            var hide = true;
+        }
+        var bbox = {};
+        try {
+            bbox = this.node.getBBox();
+        } catch(e) {
+            // Firefox 3.0.x plays badly here
+        } finally {
+            bbox = bbox || {};
+        }
+        hide && this.hide();
+        return bbox;
+    };
+    
+    elproto.attr = function (name, value) {
+        if (this.removed) {
+            return this;
+        }
+        if (name == null) {
+            var res = {};
+            for (var a in this.attrs) if (this.attrs[has](a)) {
+                res[a] = this.attrs[a];
+            }
+            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
+            res.transform = this._.transform;
+            return res;
+        }
+        if (value == null && R.is(name, "string")) {
+            if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
+                return this.attrs.gradient;
+            }
+            if (name == "transform") {
+                return this._.transform;
+            }
+            var names = name.split(separator),
+                out = {};
+            for (var i = 0, ii = names.length; i < ii; i++) {
+                name = names[i];
+                if (name in this.attrs) {
+                    out[name] = this.attrs[name];
+                } else if (R.is(this.paper.customAttributes[name], "function")) {
+                    out[name] = this.paper.customAttributes[name].def;
+                } else {
+                    out[name] = R._availableAttrs[name];
+                }
+            }
+            return ii - 1 ? out : out[names[0]];
+        }
+        if (value == null && R.is(name, "array")) {
+            out = {};
+            for (i = 0, ii = name.length; i < ii; i++) {
+                out[name[i]] = this.attr(name[i]);
+            }
+            return out;
+        }
+        if (value != null) {
+            var params = {};
+            params[name] = value;
+        } else if (name != null && R.is(name, "object")) {
+            params = name;
+        }
+        for (var key in params) {
+            eve("attr." + key + "." + this.id, this, params[key]);
+        }
+        for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
+            var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
+            this.attrs[key] = params[key];
+            for (var subkey in par) if (par[has](subkey)) {
+                params[subkey] = par[subkey];
+            }
+        }
+        setFillAndStroke(this, params);
+        return this;
+    };
+    
+    elproto.toFront = function () {
+        if (this.removed) {
+            return this;
+        }
+        this.node.parentNode.appendChild(this.node);
+        var svg = this.paper;
+        svg.top != this && R._tofront(this, svg);
+        return this;
+    };
+    
+    elproto.toBack = function () {
+        if (this.removed) {
+            return this;
+        }
+        if (this.node.parentNode.firstChild != this.node) {
+            this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
+            R._toback(this, this.paper);
+            var svg = this.paper;
+        }
+        return this;
+    };
+    
+    elproto.insertAfter = function (element) {
+        if (this.removed) {
+            return this;
+        }
+        var node = element.node || element[element.length - 1].node;
+        if (node.nextSibling) {
+            node.parentNode.insertBefore(this.node, node.nextSibling);
+        } else {
+            node.parentNode.appendChild(this.node);
+        }
+        R._insertafter(this, element, this.paper);
+        return this;
+    };
+    
+    elproto.insertBefore = function (element) {
+        if (this.removed) {
+            return this;
+        }
+        var node = element.node || element[0].node;
+        node.parentNode.insertBefore(this.node, node);
+        R._insertbefore(this, element, this.paper);
+        return this;
+    };
+    elproto.blur = function (size) {
+        // Experimental. No Safari support. Use it on your own risk.
+        var t = this;
+        if (+size !== 0) {
+            var fltr = $("filter"),
+                blur = $("feGaussianBlur");
+            t.attrs.blur = size;
+            fltr.id = R.createUUID();
+            $(blur, {stdDeviation: +size || 1.5});
+            fltr.appendChild(blur);
+            t.paper.defs.appendChild(fltr);
+            t._blur = fltr;
+            $(t.node, {filter: "url(#" + fltr.id + ")"});
+        } else {
+            if (t._blur) {
+                t._blur.parentNode.removeChild(t._blur);
+                delete t._blur;
+                delete t.attrs.blur;
+            }
+            t.node.removeAttribute("filter");
+        }
+    };
+    R._engine.circle = function (svg, x, y, r) {
+        var el = $("circle");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {cx: x, cy: y, r: r, fill: "none", stroke: "#000"};
+        res.type = "circle";
+        $(el, res.attrs);
+        return res;
+    };
+    R._engine.rect = function (svg, x, y, w, h, r) {
+        var el = $("rect");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000"};
+        res.type = "rect";
+        $(el, res.attrs);
+        return res;
+    };
+    R._engine.ellipse = function (svg, x, y, rx, ry) {
+        var el = $("ellipse");
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000"};
+        res.type = "ellipse";
+        $(el, res.attrs);
+        return res;
+    };
+    R._engine.image = function (svg, src, x, y, w, h) {
+        var el = $("image");
+        $(el, {x: x, y: y, width: w, height: h, preserveAspectRatio: "none"});
+        el.setAttributeNS(xlink, "href", src);
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {x: x, y: y, width: w, height: h, src: src};
+        res.type = "image";
+        return res;
+    };
+    R._engine.text = function (svg, x, y, text) {
+        var el = $("text");
+        // $(el, {x: x, y: y, "text-anchor": "middle"});
+        svg.canvas && svg.canvas.appendChild(el);
+        var res = new Element(el, svg);
+        res.attrs = {
+            x: x,
+            y: y,
+            "text-anchor": "middle",
+            text: text,
+            font: R._availableAttrs.font,
+            stroke: "none",
+            fill: "#000"
+        };
+        res.type = "text";
+        setFillAndStroke(res, res.attrs);
+        return res;
+    };
+    R._engine.setSize = function (width, height) {
+        this.width = width || this.width;
+        this.height = height || this.height;
+        this.canvas.setAttribute("width", this.width);
+        this.canvas.setAttribute("height", this.height);
+        if (this._viewBox) {
+            this.setViewBox.apply(this, this._viewBox);
+        }
+        return this;
+    };
+    R._engine.create = function () {
+        var con = R._getContainer.apply(0, arguments),
+            container = con && con.container,
+            x = con.x,
+            y = con.y,
+            width = con.width,
+            height = con.height;
+        if (!container) {
+            throw new Error("SVG container not found.");
+        }
+        var cnvs = $("svg"),
+            css = "overflow:hidden;",
+            isFloating;
+        x = x || 0;
+        y = y || 0;
+        width = width || 512;
+        height = height || 342;
+        $(cnvs, {
+            height: height,
+            version: 1.1,
+            width: width,
+            xmlns: "http://www.w3.org/2000/svg"
+        });
+        if (container == 1) {
+            cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
+            R._g.doc.body.appendChild(cnvs);
+            isFloating = 1;
+        } else {
+            cnvs.style.cssText = css + "position:relative";
+            if (container.firstChild) {
+                container.insertBefore(cnvs, container.firstChild);
+            } else {
+                container.appendChild(cnvs);
+            }
+        }
+        container = new R._Paper;
+        container.width = width;
+        container.height = height;
+        container.canvas = cnvs;
+        // plugins.call(container, container, R.fn);
+        container.clear();
+        container._left = container._top = 0;
+        isFloating && (container.renderfix = function () {});
+        container.renderfix();
+        return container;
+    };
+    R._engine.setViewBox = function (x, y, w, h, fit) {
+        eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
+        var size = mmax(w / this.width, h / this.height),
+            top = this.top,
+            aspectRatio = fit ? "meet" : "xMinYMin",
+            vb,
+            sw;
+        if (x == null) {
+            if (this._vbSize) {
+                size = 1;
+            }
+            delete this._vbSize;
+            vb = "0 0 " + this.width + S + this.height;
+        } else {
+            this._vbSize = size;
+            vb = x + S + y + S + w + S + h;
+        }
+        $(this.canvas, {
+            viewBox: vb,
+            preserveAspectRatio: aspectRatio
+        });
+        while (size && top) {
+            sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
+            top.attr({"stroke-width": sw});
+            top._.dirty = 1;
+            top._.dirtyT = 1;
+            top = top.prev;
+        }
+        this._viewBox = [x, y, w, h, !!fit];
+        return this;
+    };
+    
+    R.prototype.renderfix = function () {
+        var cnvs = this.canvas,
+            s = cnvs.style,
+            pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix(),
+            left = -pos.e % 1,
+            top = -pos.f % 1;
+        if (left || top) {
+            if (left) {
+                this._left = (this._left + left) % 1;
+                s.left = this._left + "px";
+            }
+            if (top) {
+                this._top = (this._top + top) % 1;
+                s.top = this._top + "px";
+            }
+        }
+    };
+    
+    R.prototype.clear = function () {
+        R.eve("clear", this);
+        var c = this.canvas;
+        while (c.firstChild) {
+            c.removeChild(c.firstChild);
+        }
+        this.bottom = this.top = null;
+        (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
+        c.appendChild(this.desc);
+        c.appendChild(this.defs = $("defs"));
+    };
+    
+    R.prototype.remove = function () {
+        eve("remove", this);
+        this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
+        for (var i in this) {
+            this[i] = removed(i);
+        }
+    };
+    var setproto = R.st;
+    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
+        setproto[method] = (function (methodname) {
+            return function () {
+                var arg = arguments;
+                return this.forEach(function (el) {
+                    el[methodname].apply(el, arg);
+                });
+            };
+        })(method);
+    }
+}(window.Raphael);
+
+// ┌─────────────────────────────────────────────────────────────────────┐ \\
+// │ Raphaël 2 - JavaScript Vector Library                               │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ VML Module                                                          │ \\
+// ├─────────────────────────────────────────────────────────────────────┤ \\
+// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
+// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
+// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
+// └─────────────────────────────────────────────────────────────────────┘ \\
+window.Raphael.vml && function (R) {
+    var has = "hasOwnProperty",
+        Str = String,
+        toFloat = parseFloat,
+        math = Math,
+        round = math.round,
+        mmax = math.max,
+        mmin = math.min,
+        abs = math.abs,
+        fillString = "fill",
+        separator = /[, ]+/,
+        eve = R.eve,
+        ms = " progid:DXImageTransform.Microsoft",
+        S = " ",
+        E = "",
+        map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},
+        bites = /([clmz]),?([^clmz]*)/gi,
+        blurregexp = / progid:\S+Blur\([^\)]+\)/g,
+        val = /-?[^,\s-]+/g,
+        cssDot = "position:absolute;left:0;top:0;width:1px;height:1px",
+        zoom = 21600,
+        pathTypes = {path: 1, rect: 1, image: 1},
+        ovalTypes = {circle: 1, ellipse: 1},
+        path2vml = function (path) {
+            var total =  /[ahqstv]/ig,
+                command = R._pathToAbsolute;
+            Str(path).match(total) && (command = R._path2curve);
+            total = /[clmz]/g;
+            if (command == R._pathToAbsolute && !Str(path).match(total)) {
+                var res = Str(path).replace(bites, function (all, command, args) {
+                    var vals = [],
+                        isMove = command.toLowerCase() == "m",
+                        res = map[command];
+                    args.replace(val, function (value) {
+                        if (isMove && vals.length == 2) {
+                            res += vals + map[command == "m" ? "l" : "L"];
+                            vals = [];
+                        }
+                        vals.push(round(value * zoom));
+                    });
+                    return res + vals;
+                });
+                return res;
+            }
+            var pa = command(path), p, r;
+            res = [];
+            for (var i = 0, ii = pa.length; i < ii; i++) {
+                p = pa[i];
+                r = pa[i][0].toLowerCase();
+                r == "z" && (r = "x");
+                for (var j = 1, jj = p.length; j < jj; j++) {
+                    r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
+                }
+                res.push(r);
+            }
+            return res.join(S);
+        },
+        compensation = function (deg, dx, dy) {
+            var m = R.matrix();
+            m.rotate(-deg, .5, .5);
+            return {
+                dx: m.x(dx, dy),
+                dy: m.y(dx, dy)
+            };
+        },
+        setCoords = function (p, sx, sy, dx, dy, deg) {
+            var _ = p._,
+                m = p.matrix,
+                fillpos = _.fillpos,
+                o = p.node,
+                s = o.style,
+                y = 1,
+                flip = "",
+                dxdy,
+                kx = zoom / sx,
+                ky = zoom / sy;
+            s.visibility = "hidden";
+            if (!sx || !sy) {
+                return;
+            }
+            o.coordsize = abs(kx) + S + abs(ky);
+            s.rotation = deg * (sx * sy < 0 ? -1 : 1);
+            if (deg) {
+                var c = compensation(deg, dx, dy);
+                dx = c.dx;
+                dy = c.dy;
+            }
+            sx < 0 && (flip += "x");
+            sy < 0 && (flip += " y") && (y = -1);
+            s.flip = flip;
+            o.coordorigin = (dx * -kx) + S + (dy * -ky);
+            if (fillpos || _.fillsize) {
+                var fill = o.getElementsByTagName(fillString);
+                fill = fill && fill[0];
+                o.removeChild(fill);
+                if (fillpos) {
+                    c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
+                    fill.position = c.dx * y + S + c.dy * y;
+                }
+                if (_.fillsize) {
+                    fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
+                }
+                o.appendChild(fill);
+            }
+            s.visibility = "visible";
+        };
+    R.toString = function () {
+        return  "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
+    };
+    addArrow = function (o, value, isEnd) {
+        var values = Str(value).toLowerCase().split("-"),
+            se = isEnd ? "end" : "start",
+            i = values.length,
+            type = "classic",
+            w = "medium",
+            h = "medium";
+        while (i--) {
+            switch (values[i]) {
+                case "block":
+                case "classic":
+                case "oval":
+                case "diamond":
+                case "open":
+                case "none":
+                    type = values[i];
+                    break;
+                case "wide":
+                case "narrow": h = values[i]; break;
+                case "long":
+                case "short": w = values[i]; break;
+            }
+        }
+        var stroke = o.node.getElementsByTagName("stroke")[0];
+        stroke[se + "arrow"] = type;
+        stroke[se + "arrowlength"] = w;
+        stroke[se + "arrowwidth"] = h;
+    };
+    setFillAndStroke = function (o, params) {
+        // o.paper.canvas.style.display = "none";
+        o.attrs = o.attrs || {};
+        var node = o.node,
+            a = o.attrs,
+            s = node.style,
+            xy,
+            newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
+            isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
+            res = o;
+
+
+        for (var par in params) if (params[has](par)) {
+            a[par] = params[par];
+        }
+        if (newpath) {
+            a.path = R._getPath[o.type](o);
+            o._.dirty = 1;
+        }
+        params.href && (node.href = params.href);
+        params.title && (node.title = params.title);
+        params.target && (node.target = params.target);
+        params.cursor && (s.cursor = params.cursor);
+        "blur" in params && o.blur(params.blur);
+        if (params.path && o.type == "path" || newpath) {
+            node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
+            if (o.type == "image") {
+                o._.fillpos = [a.x, a.y];
+                o._.fillsize = [a.width, a.height];
+                setCoords(o, 1, 1, 0, 0, 0);
+            }
+        }
+        "transform" in params && o.transform(params.transform);
+        if (isOval) {
+            var cx = +a.cx,
+                cy = +a.cy,
+                rx = +a.rx || +a.r || 0,
+                ry = +a.ry || +a.r || 0;
+            node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
+        }
+        if ("clip-rect" in params) {
+            var rect = Str(params["clip-rect"]).split(separator);
+            if (rect.length == 4) {
+                rect[2] = +rect[2] + (+rect[0]);
+                rect[3] = +rect[3] + (+rect[1]);
+                var div = node.clipRect || R._g.doc.createElement("div"),
+                    dstyle = div.style;
+                dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
+                if (!node.clipRect) {
+                    dstyle.position = "absolute";
+                    dstyle.top = 0;
+                    dstyle.left = 0;
+                    dstyle.width = o.paper.width + "px";
+                    dstyle.height = o.paper.height + "px";
+                    node.parentNode.insertBefore(div, node);
+                    div.appendChild(node);
+                    node.clipRect = div;
+                }
+            }
+            if (!params["clip-rect"]) {
+                node.clipRect && (node.clipRect.style.clip = E);
+            }
+        }
+        if (o.textpath) {
+            var textpathStyle = o.textpath.style;
+            params.font && (textpathStyle.font = params.font);
+            params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
+            params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
+            params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
+            params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
+        }
+        if ("arrow-start" in params) {
+            addArrow(res, params["arrow-start"]);
+        }
+        if ("arrow-end" in params) {
+            addArrow(res, params["arrow-end"], 1);
+        }
+        if (params.opacity != null || 
+            params["stroke-width"] != null ||
+            params.fill != null ||
+            params.src != null ||
+            params.stroke != null ||
+            params["stroke-width"] != null ||
+            params["stroke-opacity"] != null ||
+            params["fill-opacity"] != null ||
+            params["stroke-dasharray"] != null ||
+            params["stroke-miterlimit"] != null ||
+            params["stroke-linejoin"] != null ||
+            params["stroke-linecap"] != null) {
+            var fill = node.getElementsByTagName(fillString),
+                newfill = false;
+            fill = fill && fill[0];
+            !fill && (newfill = fill = createNode(fillString));
+            if (o.type == "image" && params.src) {
+                fill.src = params.src;
+            }
+            params.fill && (fill.on = true);
+            if (fill.on == null || params.fill == "none" || params.fill === null) {
+                fill.on = false;
+            }
+            if (fill.on && params.fill) {
+                var isURL = Str(params.fill).match(R._ISURL);
+                if (isURL) {
+                    fill.parentNode == node && node.removeChild(fill);
+                    fill.rotate = true;
+                    fill.src = isURL[1];
+                    fill.type = "tile";
+                    var bbox = o.getBBox(1);
+                    fill.position = bbox.x + S + bbox.y;
+                    o._.fillpos = [bbox.x, bbox.y];
+
+                    R._preload(isURL[1], function () {
+                        o._.fillsize = [this.offsetWidth, this.offsetHeight];
+                    });
+                } else {
+                    fill.color = R.getRGB(params.fill).hex;
+                    fill.src = E;
+                    fill.type = "solid";
+                    if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
+                        a.fill = "none";
+                        a.gradient = params.fill;
+                        fill.rotate = false;
+                    }
+                }
+            }
+            if ("fill-opacity" in params || "opacity" in params) {
+                var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
+                opacity = mmin(mmax(opacity, 0), 1);
+                fill.opacity = opacity;
+                if (fill.src) {
+                    fill.color = "none";
+                }
+            }
+            node.appendChild(fill);
+            var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
+            newstroke = false;
+            !stroke && (newstroke = stroke = createNode("stroke"));
+            if ((params.stroke && params.stroke != "none") ||
+                params["stroke-width"] ||
+                params["stroke-opacity"] != null ||
+                params["stroke-dasharray"] ||
+                params["stroke-miterlimit"] ||
+                params["stroke-linejoin"] ||
+                params["stroke-linecap"]) {
+                stroke.on = true;
+            }
+            (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
+            var strokeColor = R.getRGB(params.stroke);
+            stroke.on && params.stroke && (stroke.color = strokeColor.hex);
+            opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
+            var width = (toFloat(params["stroke-width"]) || 1) * .75;
+            opacity = mmin(mmax(opacity, 0), 1);
+            params["stroke-width"] == null && (width = a["stroke-width"]);
+            params["stroke-width"] && (stroke.weight = width);
+            width && width < 1 && (opacity *= width) && (stroke.weight = 1);
+            stroke.opacity = opacity;
+        
+            params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
+            stroke.miterlimit = params["stroke-miterlimit"] || 8;
+            params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
+            if (params["stroke-dasharray"]) {
+                var dasharray = {
+                    "-": "shortdash",
+                    ".": "shortdot",
+                    "-.": "shortdashdot",
+                    "-..": "shortdashdotdot",
+                    ". ": "dot",
+                    "- ": "dash",
+                    "--": "longdash",
+                    "- .": "dashdot",
+                    "--.": "longdashdot",
+                    "--..": "longdashdotdot"
+                };
+                stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
+            }
+            newstroke && node.appendChild(stroke);
+        }
+        if (res.type == "text") {
+            res.paper.canvas.style.display = E;
+            var span = res.paper.span,
+                m = 100,
+                fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
+            s = span.style;
+            a.font && (s.font = a.font);
+            a["font-family"] && (s.fontFamily = a["font-family"]);
+            a["font-weight"] && (s.fontWeight = a["font-weight"]);
+            a["font-style"] && (s.fontStyle = a["font-style"]);
+            fontSize = toFloat(fontSize ? fontSize[0] : a["font-size"]);
+            s.fontSize = fontSize * m + "px";
+            res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
+            var brect = span.getBoundingClientRect();
+            res.W = a.w = (brect.right - brect.left) / m;
+            res.H = a.h = (brect.bottom - brect.top) / m;
+            // res.paper.canvas.style.display = "none";
+            res.X = a.x;
+            res.Y = a.y + res.H / 2;
+
+            ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
+            var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
+            for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
+                res._.dirty = 1;
+                break;
+            }
+        
+            // text-anchor emulation
+            switch (a["text-anchor"]) {
+                case "start":
+                    res.textpath.style["v-text-align"] = "left";
+                    res.bbx = res.W / 2;
+                break;
+                case "end":
+                    res.textpath.style["v-text-align"] = "right";
+                    res.bbx = -res.W / 2;
+                break;
+                default:
+                    res.textpath.style["v-text-align"] = "center";
+                    res.bbx = 0;
+                break;
+            }
+            res.textpath.style["v-text-kern"] = true;
+        }
+        // res.paper.canvas.style.display = E;
+    };
+    addGradientFill = function (o, gradient, fill) {
+        o.attrs = o.attrs || {};
+        var attrs = o.attrs,
+            pow = Math.pow,
+            opacity,
+            oindex,
+            type = "linear",
+            fxfy = ".5 .5";
+        o.attrs.gradient = gradient;
+        gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) {
+            type = "radial";
+            if (fx && fy) {
+                fx = toFloat(fx);
+                fy = toFloat(fy);
+                pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
+                fxfy = fx + S + fy;
+            }
+            return E;
+        });
+        gradient = gradient.split(/\s*\-\s*/);
+        if (type == "linear") {
+            var angle = gradient.shift();
+            angle = -toFloat(angle);
+            if (isNaN(angle)) {
+                return null;
+            }
+        }
+        var dots = R._parseDots(gradient);
+        if (!dots) {
+            return null;
+        }
+        o = o.shape || o.node;
+        if (dots.length) {
+            o.removeChild(fill);
+            fill.on = true;
+            fill.method = "none";
+            fill.color = dots[0].color;
+            fill.color2 = dots[dots.length - 1].color;
+            var clrs = [];
+            for (var i = 0, ii = dots.length; i < ii; i++) {
+                dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
+            }
+            fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
+            if (type == "radial") {
+                fill.type = "gradientTitle";
+                fill.focus = "100%";
+                fill.focussize = "0 0";
+                fill.focusposition = fxfy;
+                fill.angle = 0;
+            } else {
+                // fill.rotate= true;
+                fill.type = "gradient";
+                fill.angle = (270 - angle) % 360;
+            }
+            o.appendChild(fill);
+        }
+        return 1;
+    };
+    Element = function (node, vml) {
+        this[0] = this.node = node;
+        node.raphael = true;
+        this.id = R._oid++;
+        node.raphaelid = this.id;
+        this.X = 0;
+        this.Y = 0;
+        this.attrs = {};
+        this.paper = vml;
+        this.matrix = R.matrix();
+        this._ = {
+            transform: [],
+            sx: 1,
+            sy: 1,
+            dx: 0,
+            dy: 0,
+            deg: 0,
+            dirty: 1,
+            dirtyT: 1
+        };
+        !vml.bottom && (vml.bottom = this);
+        this.prev = vml.top;
+        vml.top && (vml.top.next = this);
+        vml.top = this;
+        this.next = null;
+    };
+    var elproto = R.el;
+
+    Element.prototype = elproto;
+    elproto.constructor = Element;
+    elproto.transform = function (tstr) {
+        if (tstr == null) {
+            return this._.transform;
+        }
+        var vbs = this.paper._viewBoxShift,
+            vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
+            oldt;
+        if (vbs) {
+            oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
+        }
+        R._extractTransform(this, vbt + tstr);
+        var matrix = this.matrix.clone(),
+            skew = this.skew,
+            o = this.node,
+            split,
+            isGrad = ~Str(this.attrs.fill).indexOf("-"),
+            isPatt = !Str(this.attrs.fill).indexOf("url(");
+        matrix.translate(-.5, -.5);
+        if (isPatt || isGrad || this.type == "image") {
+            skew.matrix = "1 0 0 1";
+            skew.offset = "0 0";
+            split = matrix.split();
+            if ((isGrad && split.noRotation) || !split.isSimple) {
+                o.style.filter = matrix.toFilter();
+                var bb = this.getBBox(),
+                    bbt = this.getBBox(1),
+                    dx = bb.x - bbt.x,
+                    dy = bb.y - bbt.y;
+                o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
+                setCoords(this, 1, 1, dx, dy, 0);
+            } else {
+                o.style.filter = E;
+                setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
+            }
+        } else {
+            o.style.filter = E;
+            skew.matrix = Str(matrix);
+            skew.offset = matrix.offset();
+        }
+        oldt && (this._.transform = oldt);
+        return this;
+    };
+    elproto.rotate = function (deg, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        if (deg == null) {
+            return;
+        }
+        deg = Str(deg).split(separator);
+        if (deg.length - 1) {
+            cx = toFloat(deg[1]);
+            cy = toFloat(deg[2]);
+        }
+        deg = toFloat(deg[0]);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+            cx = bbox.x + bbox.width / 2;
+            cy = bbox.y + bbox.height / 2;
+        }
+        this._.dirtyT = 1;
+        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
+        return this;
+    };
+    elproto.translate = function (dx, dy) {
+        if (this.removed) {
+            return this;
+        }
+        dx = Str(dx).split(separator);
+        if (dx.length - 1) {
+            dy = toFloat(dx[1]);
+        }
+        dx = toFloat(dx[0]) || 0;
+        dy = +dy || 0;
+        if (this._.bbox) {
+            this._.bbox.x += dx;
+            this._.bbox.y += dy;
+        }
+        this.transform(this._.transform.concat([["t", dx, dy]]));
+        return this;
+    };
+    elproto.scale = function (sx, sy, cx, cy) {
+        if (this.removed) {
+            return this;
+        }
+        sx = Str(sx).split(separator);
+        if (sx.length - 1) {
+            sy = toFloat(sx[1]);
+            cx = toFloat(sx[2]);
+            cy = toFloat(sx[3]);
+            isNaN(cx) && (cx = null);
+            isNaN(cy) && (cy = null);
+        }
+        sx = toFloat(sx[0]);
+        (sy == null) && (sy = sx);
+        (cy == null) && (cx = cy);
+        if (cx == null || cy == null) {
+            var bbox = this.getBBox(1);
+        }
+        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
+        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
+    
+        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
+        this._.dirtyT = 1;
+        return this;
+    };
+    elproto.hide = function () {
+        !this.removed && (this.node.style.display = "none");
+        return this;
+    };
+    elproto.show = function () {
+        !this.removed && (this.node.style.display = E);
+        return this;
+    };
+    elproto._getBBox = function () {
+        if (this.removed) {
+            return {};
+        }
+        if (this.type == "text") {
+            return {
+                x: this.X + (this.bbx || 0) - this.W / 2,
+                y: this.Y - this.H,
+                width: this.W,
+                height: this.H
+            };
+        } else {
+            return pathDimensions(this.attrs.path);
+        }
+    };
+    elproto.remove = function () {
+        if (this.removed) {
+            return;
+        }
+        this.paper.__set__ && this.paper.__set__.exclude(this);
+        R.eve.unbind("*.*." + this.id);
+        R._tear(this, this.paper);
+        this.node.parentNode.removeChild(this.node);
+        this.shape && this.shape.parentNode.removeChild(this.shape);
+        for (var i in this) {
+            delete this[i];
+        }
+        this.removed = true;
+    };
+    elproto.attr = function (name, value) {
+        if (this.removed) {
+            return this;
+        }
+        if (name == null) {
+            var res = {};
+            for (var a in this.attrs) if (this.attrs[has](a)) {
+                res[a] = this.attrs[a];
+            }
+            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
+            res.transform = this._.transform;
+            return res;
+        }
+        if (value == null && R.is(name, "string")) {
+            if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
+                return this.attrs.gradient;
+            }
+            var names = name.split(separator),
+                out = {};
+            for (var i = 0, ii = names.length; i < ii; i++) {
+                name = names[i];
+                if (name in this.attrs) {
+                    out[name] = this.attrs[name];
+                } else if (R.is(this.paper.customAttributes[name], "function")) {
+                    out[name] = this.paper.customAttributes[name].def;
+                } else {
+                    out[name] = R._availableAttrs[name];
+                }
+            }
+            return ii - 1 ? out : out[names[0]];
+        }
+        if (this.attrs && value == null && R.is(name, "array")) {
+            out = {};
+            for (i = 0, ii = name.length; i < ii; i++) {
+                out[name[i]] = this.attr(name[i]);
+            }
+            return out;
+        }
+        var params;
+        if (value != null) {
+            params = {};
+            params[name] = value;
+        }
+        value == null && R.is(name, "object") && (params = name);
+        for (var key in params) {
+            eve("attr." + key + "." + this.id, this, params[key]);
+        }
+        if (params) {
+            for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
+                var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
+                this.attrs[key] = params[key];
+                for (var subkey in par) if (par[has](subkey)) {
+                    params[subkey] = par[subkey];
+                }
+            }
+            // this.paper.canvas.style.display = "none";
+            if (params.text && this.type == "text") {
+                this.textpath.string = params.text;
+            }
+            setFillAndStroke(this, params);
+            // this.paper.canvas.style.display = E;
+        }
+        return this;
+    };
+    elproto.toFront = function () {
+        !this.removed && this.node.parentNode.appendChild(this.node);
+        this.paper && this.paper.top != this && R._tofront(this, this.paper);
+        return this;
+    };
+    elproto.toBack = function () {
+        if (this.removed) {
+            return this;
+        }
+        if (this.node.parentNode.firstChild != this.node) {
+            this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
+            R._toback(this, this.paper);
+        }
+        return this;
+    };
+    elproto.insertAfter = function (element) {
+        if (this.removed) {
+            return this;
+        }
+        if (element.constructor == R.st.constructor) {
+            element = element[element.length - 1];
+        }
+        if (element.node.nextSibling) {
+            element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
+        } else {
+            element.node.parentNode.appendChild(this.node);
+        }
+        R._insertafter(this, element, this.paper);
+        return this;
+    };
+    elproto.insertBefore = function (element) {
+        if (this.removed) {
+            return this;
+        }
+        if (element.constructor == R.st.constructor) {
+            element = element[0];
+        }
+        element.node.parentNode.insertBefore(this.node, element.node);
+        R._insertbefore(this, element, this.paper);
+        return this;
+    };
+    elproto.blur = function (size) {
+        var s = this.node.runtimeStyle,
+            f = s.filter;
+        f = f.replace(blurregexp, E);
+        if (+size !== 0) {
+            this.attrs.blur = size;
+            s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
+            s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
+        } else {
+            s.filter = f;
+            s.margin = 0;
+            delete this.attrs.blur;
+        }
+    };
+
+    R._engine.path = function (pathString, vml) {
+        var el = createNode("shape");
+        el.style.cssText = cssDot;
+        el.coordsize = zoom + S + zoom;
+        el.coordorigin = vml.coordorigin;
+        var p = new Element(el, vml),
+            attr = {fill: "none", stroke: "#000"};
+        pathString && (attr.path = pathString);
+        p.type = "path";
+        p.path = [];
+        p.Path = E;
+        setFillAndStroke(p, attr);
+        vml.canvas.appendChild(el);
+        var skew = createNode("skew");
+        skew.on = true;
+        el.appendChild(skew);
+        p.skew = skew;
+        p.transform(E);
+        return p;
+    };
+    R._engine.rect = function (vml, x, y, w, h, r) {
+        var path = R._rectPath(x, y, w, h, r),
+            res = vml.path(path),
+            a = res.attrs;
+        res.X = a.x = x;
+        res.Y = a.y = y;
+        res.W = a.width = w;
+        res.H = a.height = h;
+        a.r = r;
+        a.path = path;
+        res.type = "rect";
+        return res;
+    };
+    R._engine.ellipse = function (vml, x, y, rx, ry) {
+        var res = vml.path(),
+            a = res.attrs;
+        res.X = x - rx;
+        res.Y = y - ry;
+        res.W = rx * 2;
+        res.H = ry * 2;
+        res.type = "ellipse";
+        setFillAndStroke(res, {
+            cx: x,
+            cy: y,
+            rx: rx,
+            ry: ry
+        });
+        return res;
+    };
+    R._engine.circle = function (vml, x, y, r) {
+        var res = vml.path(),
+            a = res.attrs;
+        res.X = x - r;
+        res.Y = y - r;
+        res.W = res.H = r * 2;
+        res.type = "circle";
+        setFillAndStroke(res, {
+            cx: x,
+            cy: y,
+            r: r
+        });
+        return res;
+    };
+    R._engine.image = function (vml, src, x, y, w, h) {
+        var path = R._rectPath(x, y, w, h),
+            res = vml.path(path).attr({stroke: "none"}),
+            a = res.attrs,
+            node = res.node,
+            fill = node.getElementsByTagName(fillString)[0];
+        a.src = src;
+        res.X = a.x = x;
+        res.Y = a.y = y;
+        res.W = a.width = w;
+        res.H = a.height = h;
+        a.path = path;
+        res.type = "image";
+        fill.parentNode == node && node.removeChild(fill);
+        fill.rotate = true;
+        fill.src = src;
+        fill.type = "tile";
+        res._.fillpos = [x, y];
+        res._.fillsize = [w, h];
+        node.appendChild(fill);
+        setCoords(res, 1, 1, 0, 0, 0);
+        return res;
+    };
+    R._engine.text = function (vml, x, y, text) {
+        var el = createNode("shape"),
+            path = createNode("path"),
+            o = createNode("textpath");
+        x = x || 0;
+        y = y || 0;
+        text = text || "";
+        path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
+        path.textpathok = true;
+        o.string = Str(text);
+        o.on = true;
+        el.style.cssText = cssDot;
+        el.coordsize = zoom + S + zoom;
+        el.coordorigin = "0 0";
+        var p = new Element(el, vml),
+            attr = {
+                fill: "#000",
+                stroke: "none",
+                font: R._availableAttrs.font,
+                text: text
+            };
+        p.shape = el;
+        p.path = path;
+        p.textpath = o;
+        p.type = "text";
+        p.attrs.text = Str(text);
+        p.attrs.x = x;
+        p.attrs.y = y;
+        p.attrs.w = 1;
+        p.attrs.h = 1;
+        setFillAndStroke(p, attr);
+        el.appendChild(o);
+        el.appendChild(path);
+        vml.canvas.appendChild(el);
+        var skew = createNode("skew");
+        skew.on = true;
+        el.appendChild(skew);
+        p.skew = skew;
+        p.transform(E);
+        return p;
+    };
+    R._engine.setSize = function (width, height) {
+        var cs = this.canvas.style;
+        this.width = width;
+        this.height = height;
+        width == +width && (width += "px");
+        height == +height && (height += "px");
+        cs.width = width;
+        cs.height = height;
+        cs.clip = "rect(0 " + width + " " + height + " 0)";
+        if (this._viewBox) {
+            setViewBox.apply(this, this._viewBox);
+        }
+        return this;
+    };
+    R._engine.setViewBox = function (x, y, w, h, fit) {
+        R.eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
+        var width = this.width,
+            height = this.height,
+            size = 1 / mmax(w / width, h / height),
+            H, W;
+        if (fit) {
+            H = height / h;
+            W = width / w;
+            if (w * H < width) {
+                x -= (width - w * H) / 2 / H;
+            }
+            if (h * W < height) {
+                y -= (height - h * W) / 2 / W;
+            }
+        }
+        this._viewBox = [x, y, w, h, !!fit];
+        this._viewBoxShift = {
+            dx: -x,
+            dy: -y,
+            scale: size
+        };
+        this.forEach(function (el) {
+            el.transform("...");
+        });
+        return this;
+    };
+    var createNode,
+        initWin = function (win) {
+            var doc = win.document;
+            doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
+            try {
+                !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
+                createNode = function (tagName) {
+                    return doc.createElement('<rvml:' + tagName + ' class="rvml">');
+                };
+            } catch (e) {
+                createNode = function (tagName) {
+                    return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
+                };
+            }
+        };
+    initWin(R._g.win);
+    R._engine.create = function () {
+        var con = R._getContainer.apply(0, arguments),
+            container = con.container,
+            height = con.height,
+            s,
+            width = con.width,
+            x = con.x,
+            y = con.y;
+        if (!container) {
+            throw new Error("VML container not found.");
+        }
+        var res = new R._Paper,
+            c = res.canvas = R._g.doc.createElement("div"),
+            cs = c.style;
+        x = x || 0;
+        y = y || 0;
+        width = width || 512;
+        height = height || 342;
+        res.width = width;
+        res.height = height;
+        width == +width && (width += "px");
+        height == +height && (height += "px");
+        res.coordsize = zoom * 1e3 + S + zoom * 1e3;
+        res.coordorigin = "0 0";
+        res.span = R._g.doc.createElement("span");
+        res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
+        c.appendChild(res.span);
+        cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
+        if (container == 1) {
+            R._g.doc.body.appendChild(c);
+            cs.left = x + "px";
+            cs.top = y + "px";
+            cs.position = "absolute";
+        } else {
+            if (container.firstChild) {
+                container.insertBefore(c, container.firstChild);
+            } else {
+                container.appendChild(c);
+            }
+        }
+        // plugins.call(res, res, R.fn);
+        res.renderfix = function () {};
+        return res;
+    };
+    R.prototype.clear = function () {
+        R.eve("clear", this);
+        this.canvas.innerHTML = E;
+        this.span = R._g.doc.createElement("span");
+        this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
+        this.canvas.appendChild(this.span);
+        this.bottom = this.top = null;
+    };
+    R.prototype.remove = function () {
+        R.eve("remove", this);
+        this.canvas.parentNode.removeChild(this.canvas);
+        for (var i in this) {
+            this[i] = removed(i);
+        }
+        return true;
+    };
+
+    var setproto = R.st;
+    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
+        setproto[method] = (function (methodname) {
+            return function () {
+                var arg = arguments;
+                return this.forEach(function (el) {
+                    el[methodname].apply(el, arg);
+                });
+            };
+        })(method);
+    }
+}(window.Raphael);
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/js/libs/underscore.js	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,30 @@
+// Underscore.js 1.2.3
+// (c) 2009-2011 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the MIT license.
+// Portions of Underscore are inspired or borrowed from Prototype,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore
+(function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
+c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c,
+h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.concat,H=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,I=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&&
+define.amd?define("underscore",function(){return b}):s._=b;b.VERSION="1.2.3";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(b,a[e],e,a)===o)break}else for(e in a)if(m.call(a,e)&&c.call(b,a[e],e,a)===o)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.map===w)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});return e};b.reduce=b.foldl=b.inject=function(a,
+c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(x&&a.reduce===x)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(y&&a.reduceRight===y)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,
+c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,c,b){var e;D(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.filter===z)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(A&&a.every===A)return a.every(c,
+b);j(a,function(a,g,h){if(!(e=e&&c.call(b,a,g,h)))return o});return e};var D=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(B&&a.some===B)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return q&&a.indexOf===q?a.indexOf(c)!=-1:b=D(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a,
+d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,
+computed:b})});return e.value};b.shuffle=function(a){var c=[],b;j(a,function(a,f){f==0?c[0]=a:(b=Math.floor(Math.random()*(f+1)),c[f]=c[b],c[b]=a)});return c};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,c){var b=a.criteria,d=c.criteria;return b<d?-1:b>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=
+function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-
+1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},
+[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,
+c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(q&&a.indexOf===q)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(C&&a.lastIndexOf===C)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
+var E=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));E.prototype=a.prototype;var b=new E,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
+c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return m.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
+a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=G.apply([a],arguments);return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=
+function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=I||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments,
+1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===
+Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)==
+"[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),function(c){J(c,
+b[c]=a[c])})};var K=0;b.uniqueId=function(a){var b=K++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,
+"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},J=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);H.call(a,this._wrapped);return u(c.apply(b,
+a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped,arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/integration/allocine_dossier_independant/polemic-allocine.htm	Tue Feb 21 17:26:51 2012 +0100
@@ -0,0 +1,161 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html dir="ltr" xml:lang="fr" xmlns="http://www.w3.org/1999/xhtml" lang="fr">
+
+<head>
+<title>Metadataplayer - Polemic tweet integration test</title>
+</head>
+
+<body>
+
+  <div style="width:650px;font-family: 'Trebuchet MS', 'Helvetica', 'Arial',  'Verdana', 'sans-serif';">
+  <h1>MetaDataPlayer</h1>
+  Integration test - ALLOCINE PLAYER
+  </div>
+  
+  
+ <!-- START Integration  ###################################### -->
+ <!-- SIMPLE PLAYER EXPERIMENTATION -->
+  <script type="text/javascript" src="js/LdtPlayer-release.js" type="text/javascript"></script>   
+  
+  <div id="video"></div>
+  <div id="LdtPlayer"></div>
+  <div id="Sparkline"></div>
+  <script  type="text/javascript">
+    var json_url = 'allocine_test/exemple.json';
+    
+    IriSP.user = {name: 'awesome_user_name', avatar: 'allocine_test/avatar.png',};
+    IriSP.libdir = "js/libs/";
+    IriSP.widgetsDefaults["createAnnotationWidget"] = {};
+    IriSP.widgetsDefaults["createAnnotationWidget"].cinecast_version = true;
+    IriSP.widgetsDefaults["createAnnotationWidget"].keywords = ["#allocine", "#vodkaster", "#universcine"];
+    IriSP.widgetsDefaults["createAnnotationWidget"].api_endpoint_template = "coucou/{{id}}.json";
+    IriSP.defaults.user = function(){ return IriSP.user; };
+    
+    var config = {            
+        gui:{
+            width:650,
+            height:2100,              
+            container:'LdtPlayer',
+            css:'css/LdtPlayer.css',
+            widgets: [
+            {type: "StackGraphWidget",
+                tags : [
+                    {
+                        "keywords" : [ "élève", "souleyman", "souleiman", "koumba", "kumba", "karl", "esmeralda" ],
+                        "description" : "Mentions des élèves",
+                        "color" : "#CE0A15",
+                    },
+                    {
+                        "keywords" : [ "F.M", "françois" ],
+                        "description" : "Mentions de François",
+                        "color" : "#036AAE",
+                    },
+                    {
+                        "keywords" : [ "plan", "pano", "caméra", "plongée" ],
+                        "description" : "Descriptions de plans",
+                        "color" : "#C5A62D",   
+                    },
+                ],
+               metadata:{
+                format:'cinelab',
+                src: json_url,
+                type:'json'},
+                
+               requires: [{
+                type: "TooltipWidget",
+                width: 180,
+                heigh: 160,
+                metadata : {type:'empty'
+                }
+               }],
+            },            
+            {type: "AnnotationsListWidget",
+             container: "AnnotationsListContainer",             
+                         metadata:{
+                          format:'cinelab',
+                          src:json_url,
+                          type:'json'}
+                        },
+            {type: "SparklineWidget",
+             width: 640, /* required for this widget */
+             height: 50,            
+                         metadata:{
+                          format:'cinelab',
+                          src:json_url,
+                          type:'json'}
+                        },
+            {type: "SliderWidget",
+                         metadata:{
+                          format:'cinelab',
+                          src:json_url,
+                          type:'json'}
+                        },        
+            {type: "PlayerWidget", // please note that type refers directly to the constructor of the widget.
+             mode: "radio",
+             metadata:{
+              format:'cinelab',
+              src:json_url,
+              type:'json'}
+            },
+            {type: "SegmentsWidget",
+             metadata:{
+              format:'cinelab',
+              src:json_url,
+              type:'json'},
+             requires: [{
+              type: "TooltipWidget",
+              width: 180,
+              heigh: 160,
+              metadata : {
+                type:'empty'
+              }
+             }],
+            },                     
+            {type: "ArrowWidget",
+             metadata:{
+              format:'cinelab',
+              src:json_url,
+              type:'json'}
+            },
+            {type: "TweetsWidget",
+                         metadata:{
+                          format:'cinelab',
+                          src:json_url,
+                          type:'json'}
+                        },
+            {type: "createAnnotationWidget",
+                         metadata:{
+                          format:'cinelab',
+                          src:json_url,
+                          type:'json'}
+                        }      
+            ]
+        },
+      player:{
+      type:'allocine', // player type 
+              height: 300, 
+              width: 640,
+              acPlayerUrl: "allocine_test/AcPlayer_v3.0.swf",
+              autoPlay: "true",
+              urlAcData: "allocine_test/data_cinecast.xml",
+              directVideoPath: "http://hd.fr.mediaplayer.allocine.fr/nmedia/18/86/23/92/19282986_fa3_vost_sd_001.flv"
+      },
+      modules: [
+               { type: "MediaFragment",
+                         metadata:{
+                       format:'cinelab',
+                           src:json_url,
+                             type:'json'}
+                        }]
+
+    };
+    
+    
+    IriSP.initPlayer(config, json_url);
+
+  </script>
+  
+  
+ </body>
+ <div id="AnnotationsListContainer" style="position: absolute; width: 400px; left: 660px; top: 105px;"></div>
+ </html>