]> git.parisson.com Git - telemeta.git/commitdiff
fixed and checked code methods and minor bugs
authorriccardo <riccardo@parisson.com>
Thu, 16 Jun 2011 11:45:26 +0000 (13:45 +0200)
committerriccardo <riccardo@parisson.com>
Thu, 16 Jun 2011 11:45:26 +0000 (13:45 +0200)
telemeta/htdocs/js/divmarker.js
telemeta/htdocs/js/playerLoader.js
telemeta/htdocs/timeside/js/markermap.js
telemeta/htdocs/timeside/js/player.js
telemeta/htdocs/timeside/js/ruler.js
telemeta/htdocs/timeside/js/timeside.js

index 1b6b50e094be21eb9209e64b62f45010871f389f..6d8e993af64103859a34594fd868a83c738975d9 100644 (file)
@@ -61,34 +61,40 @@ Timeside.classes.MarkerMapDiv = Timeside.classes.TimesideArray.extend({
     },
     //overridden. Do not call explicitly, use marker map.move
     move: function(from, to, newOffset){
-
+        
         //call super method
-        var realIndex = this._super(from,to);
+        to = this._super(from,to);
+        if(to<0){
+            return -1;
+        }
+
         //reflect the same changes in the document:
         var me = this.toArray();
-        if(realIndex!=from){
-            var div = me[realIndex]; //me has already been updated
+        if(to!=from){
+            var div = me[to]; //me has already been updated
             div.detach();
             var parent = this.div;
-            if(to==this.length){
+            if(to==this.length-1){
+                //consolelog('appending '+this.length+' '+parent.children().length+' '+to);
                 parent.append(div);
             }else{
-                this.$J( parent.children()[realIndex] ).before(div);
+                //consolelog('inserting '+this.length+' '+parent.children().length+' '+to);
+                this.$J( parent.children()[to] ).before(div);
             }
         }
 
         var t = this;
         var setIdx = t.setIndex;
 
-        this.each(Math.min(from,realIndex),Math.max(from,realIndex)+1, function(i, div){
+        this.each(Math.min(from,to),Math.max(from,to)+1, function(i, div){
             setIdx.apply(t,[div,i]);
         });
-        this.setOffset(me[realIndex],newOffset);
+        this.setOffset(me[to],newOffset);
 
         //TODO: create a function?
-        this.setEditMode(realIndex,true);
-        this.setFocus(realIndex,true);
-        return realIndex;
+        this.setEditMode(to,true);
+        this.setFocus(to,true);
+        return to;
     },
     //overridden
     remove : function(index){
index 30fb55c82f374bb09094ca2c7bf747aa231a1dfb..9f9808e7493195000b98cdeb119a966921e20cfc 100644 (file)
@@ -315,7 +315,7 @@ function loadPlayer(analizerUrl, soundUrl, soundImgSize, itemId, visualizers, cu
                     $J('#analyzer_div_id').find('table').find('tbody:last').append(analyzerContentArray.join(""));
 
                     //setting up the select tag
-
+                   
                     player.bind('waitShown', function(data){
                         visualizersSelectElement.hide();
                     });
@@ -338,8 +338,11 @@ function loadPlayer(analizerUrl, soundUrl, soundImgSize, itemId, visualizers, cu
                         'top':margin+'px',
                         'right':margin,
                         'margin':0
-                    }).hide(); //hide it to be sure. We could check player.isImgRefreshing but we have to think about event queue...
+                    });
 
+                    if(player.isImgRefreshing){
+                        visualizersSelectElement.hide();
+                    }
                     control.append(visualizersSelectElement);
                     //Eventually, do 3 last things:
                     //1) call end (without arguments simply clears the wait span and avoid subsequent calls to end(msg) to
@@ -406,12 +409,20 @@ function loadPlayer(analizerUrl, soundUrl, soundImgSize, itemId, visualizers, cu
                                     }
                                     popupTimeoutId=undefined;
                                     popupShowFunction(data);
-                                    var index = data.index;
-                                    //consolelog(data);
-                                    //consolelog(index+') '+data.marker.offset+' | '+(map.toArray()[index+1].offset+' - '+data.timeMarginInSec));
-                                    if(index+1 == map.length || map.toArray()[index+1].offset-data.marker.offset-data.timeMarginInSec>3){
-                                        popupTimeoutId = popupdiv.setTimeout('close',3000);
+                                    
+                                    var next = data.nextMarkerTimeInterval ? data.nextMarkerTimeInterval[0] :undefined;
+                                    if(next === undefined || next-data.currentSoundPosition > POPUP_TIMEOUT){
+                                        popupTimeoutId = popupdiv.setTimeout('close',POPUP_TIMEOUT*1000);
                                     }
+                                    
+                                //var index = data.index;
+                                //consolelog('===');
+                                //consolelog(data.currentSoundPosition);
+                                //consolelog(data.nextMarkerTimeInterval);
+                                //consolelog(index+') '+data.marker.offset+' | '+(map.toArray()[index+1].offset+' - '+data.timeMarginInSec));
+                                //if(index+1 == map.length || map.toArray()[index+1].offset-data.marker.offset-data.timeMarginInSec>3){
+                                //    popupTimeoutId = popupdiv.setTimeout('close',3000);
+                                //}
                                 //consolelog('firing markercrossed');
                                 //consolelog(data.marker.title);
                             
@@ -462,3 +473,114 @@ function loadPlayer(analizerUrl, soundUrl, soundImgSize, itemId, visualizers, cu
    
 }
 
+
+/*
+* Sets a "tab look" on some elements of the page. Takes at least 3 arguments, at most 5:
+* 1st argument: an array (or a jquery object) of html elements, ususally anchors, representing the tabs
+* 2nd argument: an array (or a jquery object) of html elements, ususally divs, representing the containers to be shown/hidden when
+*   clicking the tabs. The n-th tab will set the n-th container to visible, hiding the others. So order is important. Note that if tabs
+*   or container are jQuery objects, the html elements inside them are sorted according to the document order. That's why tabs and
+*   container can be passed also as javascript arrays, so that the binding n-th tab -> n-th container can be decided by the user
+*   regardeless on how elements are written on the page, if already present
+* 3rd argument: the selected index. If missing it defaults to zero.
+* 4th argument: selectedtab class. Applies to the selected tab after click of one tab. If missing, nothing is done
+* 5th argument the unselectedtab class. Applies to all tabs not selected after click of one tab. If missing, nothing is done
+*
+* NOTE: The last 2 arguments are mostly for customizing the tab "visual look", as some css elements (eg, (position, top, zIndex)
+* are set inside the code and cannot be changed, as they are mandatory to let tab anchor behave like desktop application tabs. Note also
+* that every tab container is set to 'visible' by means of jQuery.show()
+*
+* Examples:
+* setUpPlayerTabs([jQuery('#tab1),jQuery('#tab1)], [jQuery('#div1),jQuery('#div2)], 1);
+* sets the elements with id '#tab1' and '#tab2' as tab and assign the click events to them so that clicking tab_1 will show '#div_1'
+* (and hide '#div2') and viceversa for '#tab2'. The selected index will be 1 (second tab '#tab2')
+*/
+function setUpPlayerTabs() {//called from within controller.js once all markers have been loaded.
+    //this is because we need all divs to be visible to calculate size. selIndex is optional, it defaults to 0
+    //
+
+    var $J = jQuery;
+    var tabs_ = arguments[0];
+    var divs_ = arguments[1]; //they might be ctually any content, div is a shoertand
+
+    //converting arguments to array: tabs
+    var tabs=[];
+    if(tabs_ instanceof $J){
+        tabs_.each(function(i,elm){
+            tabs.push(elm);
+        });
+    }else{
+        tabs = tabs_;
+    }
+    //set the overflow property of the parent tab to visible, otherwise scrollbars are displayed
+    //and the trick of setting position:relative+top:1px+zIndices (see css) doesnt work)
+    $J(tabs).each(function(i,tab){
+        var t = $J(tab).attr('href','#');
+        t.show(); //might be hidden
+        //set necessary style for the tab appearence:
+        var overflow = t.parent().css('overflow');
+        if(overflow && overflow != 'visible'){
+            t.parent().css('overflow','visible');
+        }
+    });
+    //converting arguments to array: divs
+    var divs=[];
+    if(divs_ instanceof $J){
+        divs_.each(function(i,elm){
+            divs.push(elm);
+        });
+    }else{
+        divs = divs_;
+    }
+
+    //reading remaing arguments (if any)
+    var selIndex = arguments.length>2 ? arguments[2] : 0;
+    var selectedTabClass = arguments.length>3 ? arguments[3] : undefined;
+    var unselectedTabClass = arguments.length>4 ? arguments[4] : undefined;
+
+    //function to be associate to every click on the tab (see below)
+    var tabClicked = function(index) {
+        for(var i=0; i<tabs.length; i++){
+            var t = $J(tabs[i]);
+
+            var div = $J(divs[i]);
+            var addClass = i==index ? selectedTabClass : unselectedTabClass;
+            var removeClass = i==index ? unselectedTabClass : selectedTabClass;
+            if(removeClass){
+                t.removeClass(removeClass);
+            }
+            if(addClass){
+                t.addClass(addClass);
+            }
+
+            //relevant css. Will override any css set in stylesheets
+            t.css({
+                'position':'relative',
+                'top':'1px',
+                'zIndex': (i==index ? '10' : '0')
+            });
+
+            if(i===index){
+                div.fadeIn('slow');
+            }else{
+                div.hide();
+            }
+        }
+    };
+
+    //bind clicks on tabs to the function just created
+    for (var i=0;i<tabs.length;i++){
+        // introduce a new scope (round brackets)
+        //otherwise i is retrieved from the current scope and will be always equal to tabs.length
+        //due to this loop
+        (function(tabIndex){
+            $J(tabs[i]).click(function(){
+                tabClicked(tabIndex);
+                return false;//returning false avoids scroll of the anchor to the top of the page
+            });
+        })(i);
+    }
+
+    //select the tab
+    $(tabs[selIndex]).trigger("click");
+}
index 0a17831e92e2829555f9c19f34ae76ac0cabc17c..99fb3b2811fda8acd0685134ad6a90aef28ec62e 100644 (file)
@@ -115,24 +115,35 @@ Timeside.classes.MarkerMap = Timeside.classes.TimesideArray.extend({
 
         var newIndex = this.insertionIndex(newOffset);
         //select the case:
-        if(newIndex<0){
+        if(newIndex<0){ //newindex should ALWAYS be lower than zero, as insertionIndex(number) should not match any marker
             //we didn't move the marker on another marker (newOffset does not correspond to any marker)
             //just return the real insertionIndex
             newIndex = -newIndex-1;
         }
+        
+        //now: if the isnertionIndex is greater than the marker index (oldIndex), 
+        //we decrement newIndex cause super.move will first REMOVE the marker and then set it at newIndex
+        if(newIndex > oldIndex){
+            newIndex--;
+        }
 
         //realIndex is the REAL INDEX AT WHICH WILL BE the moving marker M AFTER move.
         //It newIndex = oldIndex+1
         //we move M IMMEDIATELY AFTER ITSELF, which means, after removing M, that M has realIndex=n, as before
-        var realIndex = this._super(oldIndex,newIndex);
+       
+        newIndex = this._super(oldIndex,newIndex);
+        
+        if(newIndex <0){
+            this.debug('markermap.move: index out of bounds');
+            return -1;
+        }
         
         var markers = this.toArray();
-        var marker = markers[realIndex];
+        var marker = markers[newIndex];
         var oldOffset = marker.offset;
         marker.offset = newOffset;
         this.fire('move', {
             marker: marker,
-            index: realIndex,
             fromIndex: oldIndex,
             toIndex: newIndex,
             oldOffset: oldOffset
index 30ad305ceb0b15732641f124dd384c58aade267b..df4b8b5b2b5311b1ad6291ca4e55389539b48ff5 100644 (file)
@@ -380,7 +380,9 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
             ruler.movePointer(newPositionInSeconds);
         }
         //set sound position:
+        var oldSoundPosition = this.soundPosition;
         this.soundPosition = newPositionInSeconds;
+        
         //resume playing if it was playing:
         if(wasPlaying){
             var player = this;
@@ -388,10 +390,11 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
             //delay a little bit the play resume, this might avoid fast pointer repositioning
             //(it should not be the case, but it happens. why??)
             setTimeout(function(){
-                //player.playState = 1; //set to loading, so we do not display waitbar 'loading' again
                 player.play.apply(player);
             },100);
         }
+        this.fire('soundPositionSet',{player:this,oldSoundPosition:oldSoundPosition});
+
     },
     //given a marker at index I, specifies that a marker corss event is fired whenever the sound position (pointer)
     //is in the interval ]markerCrossedOffsetMargin-I,I+markerCrossedOffsetMargin[
@@ -439,25 +442,30 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
                 var data = { //if you change data, change it also below
                     index:idx,
                     marker:marker,
-                    timeMarginInSec: margin
+                    currentSoundPosition: player.soundPosition,
+                    nextMarkerTimeInterval: undefined
                 };
                 fireOnMarkerPosition = function(seconds){
                     if(marker){
                         if(seconds>intervalLowerBound && seconds < intervalUpperBound){
-                            player.fire('markerCrossed',data);
                             idx++;
+                            marker = idx < len ?  markers[idx] : undefined;
+                            offs = marker ? marker.offset : undefined;
+                            data.currentSoundPosition = seconds;
+                            data.nextMarkerTimeInterval = marker ? [offs-margin, offs+margin] : undefined;
+                            player.fire('markerCrossed',data);
+                            
                             if(idx<len){
-                                marker = markers[idx];
-                                offs = marker.offset;
                                 intervalUpperBound =  offs+margin;
                                 intervalLowerBound =  offs-margin;
-                                data = { //if you change data, change it also above
-                                    index:idx,
-                                    marker:marker,
-                                    timeMarginInSec: margin
-                                };
-                            }else{
-                                marker = undefined;
+                                data.index = idx;
+                                data.marker = marker;
+//                                { //if you change data, change it also above
+//                                    index:idx,
+//                                    marker:marker
+//                                };
+//                            }else{
+//                                marker = undefined;
                             }
                         }
                     }
@@ -472,19 +480,30 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
         var loadingString = this.msgs.loading;
         
         var updateWaitBar = this.setWait;
-
-        if(loadingString && !this.playState){ //
+        //building immediately data events to be passed instead of bulding them in the loop whileplaying
+        var loadData = {player:this,oldPlayState:0, endOfPlayback:false};
+        var bufferData = {player:this,oldPlayState:0, endOfPlayback:false};
+        var playData = {player:this,oldPlayState:0, endOfPlayback:false};
+        var endData = {player:this,oldPlayState:0, endOfPlayback:true};
+        //done
+        var playState = this.playState;
+        if(!playState){ 
+            if(loadingString){
             updateWaitBar.apply(this,[loadingString]); //calling setWait of an empty string hides the wait, we dont want it here
             //ps: without apply this in updateWait is the dom window
-            this.playState = 1;
+            }
+            loadData.oldPlayState = playState;
+            playState = this.playState = 1;
+            this.fire('playStateChanged',loadData);
         }
-        var playState = this.playState;
+        
         var playOptions = {
             position: sPosInMsec,
             whileplaying: function(){
 
                 var sPos = this.position;
                 var buffering = this.isBuffering || typeof sPos != 'number' || sPos < sPosInMsec;
+                
                 //var buffering = this.isBuffering; //this refers to the soundmanager sound obj
                 //Now, what are we doing here below? we could simply check whether is buffering or not..
                 //Unfortunately, when buffering some playState (isBuffering = false) are also fired, randomly
@@ -499,8 +518,9 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
                             case 2: //do nothing (wait element already displaying)
                                 break;
                             default: //update the wait element showing it:
-                                playState = 2;
-                                player.playState = 2;
+                                bufferData.oldPlayState = playState;
+                                player.playState = playState = 2;
+                                player.fire('playStateChanged',bufferData);
                                 if(bufferingString){
                                     updateWaitBar.apply(player,[bufferingString]);
                                 }
@@ -510,11 +530,13 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
                         switch(playState){
                             case 0:
                             case 1:
-                            case 2: //update waitbar
-                                updateWaitBar.apply(player,[player.isImgRefreshing ? player.msgs.imgRefreshing : '']);
+                            case 2:
+                                playData.oldPlayState = playState;
                                 //currentState = isPlayingId; //set state for future subsequent calls of this case
-                                playState = 3;
-                                player.playState = 3;
+                                player.playState = playState = 3;
+                                player.fire('playStateChanged',playData);
+                                //update waitbar
+                                updateWaitBar.apply(player,[player.isImgRefreshing ? player.msgs.imgRefreshing : '']);
                             default: //move pointer
                                 var sPosInSec = toSec(sPos);
                                 player.soundPosition = sPosInSec;
@@ -525,15 +547,16 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
 
             },
             onfinish: function() {
+                
                 //whileplaying is NOT called onsinfish. We must update the pointer:
                 //note that for small length sounds (wg, 5 secs) the pointer shifts abruptly from the last
                 //whileplaying position to the end. We tried with a setTimeout function but the visual effect is not
                 //removed. So we leave this small 'bug'
+                endData.oldPlayState = playState;
+                player.playState = playState = 0;
+                player.fire('playStateChanged',endData);
                 ruler.movePointer(player.getSoundDuration());
                 updateWaitBar.apply(player,[player.isImgRefreshing ? player.msgs.imgRefreshing : '']);
-                player.playState = 0;
-                player.fire('playbackEndReached');
-
             //player.fire('endReached');
             }
         };
@@ -600,9 +623,15 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
         //            }
         //        };
 
-        sound.setVolume(sound.volume); //workaround. Just to be sure. Sometimes it fails when we re-play
-        sound.play(playOptions);
-
+        //if the pointer is already at the end of sound, soundmanager does not fire onfinish but starts buffering
+        //forever. Therefore, we must check this case here.
+        //We use a margin of time of 20 milliseconds (.2 seconds) to indicate that inside this margin the sound is at its end
+        if(this.soundPosition + 0.2 >= this.getSoundDuration()){
+            playOptions.onfinish();
+        }else{
+            sound.setVolume(sound.volume); //workaround. Just to be sure. Sometimes it fails when we re-play
+            sound.play(playOptions);
+        }
 
         return false;
     },
@@ -619,10 +648,9 @@ Timeside.classes.Player = Timeside.classes.TimesideClass.extend({
         if(sound){
             var v = this.isPlaying();
             sound.stop();
+            var data = {player:this, oldPlayState:this.playState, endOfPlayback:false};
             this.playState = 0;
-            this.fire('paused',{
-                'wasPlaying':v
-            });
+            this.fire('playStateChanged',data);
             this.setWait(this.isImgRefreshing ? this.msgs.imgRefreshing : '');
         }
         return false;
index 211066d81fe35a9a6d1b9a2baca2f74c191c8c6a..585c579e9471cd023d08e7951a108825074e4851 100644 (file)
@@ -282,9 +282,12 @@ Timeside.classes.Ruler = Timeside.classes.TimesideArray.extend({
 
     //overridden: do not call directly this method, use markermap.move
     move: function(from, to, newOffset){
-        var newIndex = this._super(from,to);
+        to = this._super(from,to);
+        if(to <0){ //no move (some error)
+            return -1;
+        }
         //update label if it is the case:
-        var rulermarker = this.toArray()[newIndex];
+        var rulermarker = this.toArray()[to];
         var pixelOffset = this.toPixelOffset(newOffset);
         if(rulermarker.positionInPixels != pixelOffset){ //should not be the case if this method is called from a mouse event
             rulermarker.move(pixelOffset);
@@ -294,14 +297,15 @@ Timeside.classes.Ruler = Timeside.classes.TimesideArray.extend({
         }
 
         //this.debug('ruler.move: [from:'+from+', to:'+to+', real:'+newIndex+']');
-        if(newIndex!=from){
-            var i1 = Math.min(from,newIndex);
-            var i2 = Math.max(from,newIndex)+1;
+        if(to!=from){
+            var i1 = Math.min(from,to);
+            var i2 = Math.max(from,to)+1;
             //this.debug('updating ['+i1+','+i2+']');
             this.each(i1,i2, function(index,rulermarker){
                 rulermarker.setIndex(index, true);
             });
         }
+        return to;
     },
     //overridden
     //add(offset.-1) adds the pointer, isMovable is ingored
index 05c051cdf85bc1fb53a457930ed7a2026ff609f1..aeb262d3e306c387d828307364045018758ad007 100644 (file)
@@ -241,9 +241,12 @@ Timeside.classes.TimesideClass = Timeside.Class.extend({
     fire : function(key, dataArgument){
         var listenersMap = this.listenersMap;
         if(!(key in listenersMap)){
-            this.debug('"'+key+'" fired but no binding associated to it');
+            //this.debug('"'+key+'" fired but no binding associated to it');
             return;
         }
+        if(arguments.length < 2 || !dataArgument){
+            dataArgument = {};
+        }
         var callbacks = listenersMap[key];
         var len = callbacks && callbacks.length ? callbacks.length : 0;
         for(var i=0; i<len; i++){
@@ -400,248 +403,248 @@ Timeside.classes.TimesideClass = Timeside.Class.extend({
         }
         return attr;
     },
-    //map to store each class name to the relative sictionary for raphael attr function (VML only)
-    classToRaphaelAttr : {},
+    //map to store each class name to the relative dictionary for raphael attr function (VML only)
+    classToRaphaelAttr : {} //,
 
     //css specific functions:
     //get computed style first (cross browser solution, from http://blog.stchur.com/2006/06/21/css-computed-style/
     //not used anymnore, we keep it here because it can be useful
-    getComputedStyle : function(_elem, _style){
-        var computedStyle;
-        var $J = this.$J;
-        if(_elem instanceof $J){ //note: '_elem instanceof this.$J' doesnt work. why??
-            _elem = _elem.get(0);
-        }
-        if (typeof _elem.currentStyle != 'undefined'){
-            computedStyle = _elem.currentStyle;
-        }else{
-            computedStyle = document.defaultView.getComputedStyle(_elem, null);
-        }
-        return computedStyle[_style];
-    },
-    //returns a hex color from strColor. strColor can be one of the predefined css colors (eg, 'aliceBlue', case insensitive)
-    //or rgb (eg: rgb(12,0,45)) or an hex color (eg, '#ff98a3' or '#f8g'). Leading and trailing spaces will be omitted,
-    //case is insensitive, an empty string is returned in case of error (bad format string, parseInt errors etcetera..)
-    color : function(strColor){
-        if(!strColor){
-            return '';
-        }
-        strColor = strColor.replace(/(^\s+|\s+$)/g,'').toLowerCase();
-        if(!strColor){
-            return '';
-        }
-        var predefined_colors = {
-            aliceblue:"#f0f8ff",
-            antiquewhite:"#faebd7",
-            aqua:"#00ffff",
-            aquamarine:"#7fffd4",
-            azure:"#f0ffff",
-            beige:"#f5f5dc",
-            bisque:"#ffe4c4",
-            black:"#000000",
-            blanchedalmond:"#ffebcd",
-            blue:"#0000ff",
-            blueviolet:"#8a2be2",
-            brown:"#a52a2a",
-            burlywood:"#deb887",
-            cadetblue:"#5f9ea0",
-            chartreuse:"#7fff00",
-            chocolate:"#d2691e",
-            coral:"#ff7f50",
-            cornflowerblue:"#6495ed",
-            cornsilk:"#fff8dc",
-            crimson:"#dc143c",
-            cyan:"#00ffff",
-            darkblue:"#00008b",
-            darkcyan:"#008b8b",
-            darkgoldenrod:"#b8860b",
-            darkgray:"#a9a9a9",
-            darkgrey:"#a9a9a9",
-            darkgreen:"#006400",
-            darkkhaki:"#bdb76b",
-            darkmagenta:"#8b008b",
-            darkolivegreen:"#556b2f",
-            darkorange:"#ff8c00",
-            darkorchid:"#9932cc",
-            darkred:"#8b0000",
-            darksalmon:"#e9967a",
-            darkseagreen:"#8fbc8f",
-            darkslateblue:"#483d8b",
-            darkslategray:"#2f4f4f",
-            darkslategrey:"#2f4f4f",
-            darkturquoise:"#00ced1",
-            darkviolet:"#9400d3",
-            deeppink:"#ff1493",
-            deepskyblue:"#00bfff",
-            dimgray:"#696969",
-            dimgrey:"#696969",
-            dodgerblue:"#1e90ff",
-            firebrick:"#b22222",
-            floralwhite:"#fffaf0",
-            forestgreen:"#228b22",
-            fuchsia:"#ff00ff",
-            gainsboro:"#dcdcdc",
-            ghostwhite:"#f8f8ff",
-            gold:"#ffd700",
-            goldenrod:"#daa520",
-            gray:"#808080",
-            grey:"#808080",
-            green:"#008000",
-            greenyellow:"#adff2f",
-            honeydew:"#f0fff0",
-            hotpink:"#ff69b4",
-            indianred:"#cd5c5c",
-            indigo:"#4b0082",
-            ivory:"#fffff0",
-            khaki:"#f0e68c",
-            lavender:"#e6e6fa",
-            lavenderblush:"#fff0f5",
-            lawngreen:"#7cfc00",
-            lemonchiffon:"#fffacd",
-            lightblue:"#add8e6",
-            lightcoral:"#f08080",
-            lightcyan:"#e0ffff",
-            lightgoldenrodyellow:"#fafad2",
-            lightgray:"#d3d3d3",
-            lightgrey:"#d3d3d3",
-            lightgreen:"#90ee90",
-            lightpink:"#ffb6c1",
-            lightsalmon:"#ffa07a",
-            lightseagreen:"#20b2aa",
-            lightskyblue:"#87cefa",
-            lightslategray:"#778899",
-            lightslategrey:"#778899",
-            lightsteelblue:"#b0c4de",
-            lightyellow:"#ffffe0",
-            lime:"#00ff00",
-            limegreen:"#32cd32",
-            linen:"#faf0e6",
-            magenta:"#ff00ff",
-            maroon:"#800000",
-            mediumaquamarine:"#66cdaa",
-            mediumblue:"#0000cd",
-            mediumorchid:"#ba55d3",
-            mediumpurple:"#9370d8",
-            mediumseagreen:"#3cb371",
-            mediumslateblue:"#7b68ee",
-            mediumspringgreen:"#00fa9a",
-            mediumturquoise:"#48d1cc",
-            mediumvioletred:"#c71585",
-            midnightblue:"#191970",
-            mintcream:"#f5fffa",
-            mistyrose:"#ffe4e1",
-            moccasin:"#ffe4b5",
-            navajowhite:"#ffdead",
-            navy:"#000080",
-            oldlace:"#fdf5e6",
-            olive:"#808000",
-            olivedrab:"#6b8e23",
-            orange:"#ffa500",
-            orangered:"#ff4500",
-            orchid:"#da70d6",
-            palegoldenrod:"#eee8aa",
-            palegreen:"#98fb98",
-            paleturquoise:"#afeeee",
-            palevioletred:"#d87093",
-            papayawhip:"#ffefd5",
-            peachpuff:"#ffdab9",
-            peru:"#cd853f",
-            pink:"#ffc0cb",
-            plum:"#dda0dd",
-            powderblue:"#b0e0e6",
-            purple:"#800080",
-            red:"#ff0000",
-            rosybrown:"#bc8f8f",
-            royalblue:"#4169e1",
-            saddlebrown:"#8b4513",
-            salmon:"#fa8072",
-            sandybrown:"#f4a460",
-            seagreen:"#2e8b57",
-            seashell:"#fff5ee",
-            sienna:"#a0522d",
-            silver:"#c0c0c0",
-            skyblue:"#87ceeb",
-            slateblue:"#6a5acd",
-            slategray:"#708090",
-            slategrey:"#708090",
-            snow:"#fffafa",
-            springgreen:"#00ff7f",
-            steelblue:"#4682b4",
-            tan:"#d2b48c",
-            teal:"#008080",
-            thistle:"#d8bfd8",
-            tomato:"#ff6347",
-            turquoise:"#40e0d0",
-            violet:"#ee82ee",
-            wheat:"#f5deb3",
-            white:"#ffffff",
-            whitesmoke:"#f5f5f5",
-            yellow:"#ffff00",
-            yellowgreen:"#9acd32"
-        };
-        var cl = predefined_colors[strColor];
-        if(cl){
-            return cl;
-        }
-        var pint = parseInt;
-        //color parsers: note that the order is not random: we put first the most likely case ('#aaaaaa') and at last
-        //the less one ('rgb(...)'), this might increase performances in most cases, especially if this method is called from
-        //within a loop
-        var color_defs = [
-        {
-            re: /^#*(\w{2})(\w{2})(\w{2})$/, //example: ['#00ff00', '336699'],
-            process: function (bits){
-                return [pint(bits[1], 16),pint(bits[2], 16),pint(bits[3], 16)];
-            }
-        },
-
-        {
-            re: /^#*(\w{1})(\w{1})(\w{1})$/, //example: ['#fb0', 'f0f'],
-            process: function (bits){
-                return [pint(bits[1] + bits[1], 16),pint(bits[2] + bits[2], 16),pint(bits[3] + bits[3], 16)];
-            }
-        },
-        {
-            re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, //example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
-            process: function (bits){
-                return [pint(bits[1]), pint(bits[2]),pint(bits[3])];
-            }
-        }
-
-        ];
-
-        // search through the definitions to find a match
-        var nan = isNaN;
-        for (var i = 0; i < color_defs.length; i++) {
-            var re = color_defs[i].re;
-            var processor = color_defs[i].process;
-            var bits = re.exec(strColor);
-            if (bits) {
-                var channels = processor(bits);
-                if(channels.length ==3){
-                    for(var j=0; j<3; j++){
-                        var c = channels[j];
-                        c = (nan(c) || c< 0 ? 0 : (c>255 ? 255 :c));
-                        c = c.toString(16);
-                        var len = c.length;
-                        switch(len){
-                            case 1:
-                                c = '0'+c;
-                                break;
-                            case 2:
-                                break;
-                            default:
-                                return '';
-                        }
-                        channels[j] = c;
-                    }
-                    return '#'+channels.join('');
-                }
-            }
-        }
-        return '';
-    }
-
+//    getComputedStyle : function(_elem, _style){
+//        var computedStyle;
+//        var $J = this.$J;
+//        if(_elem instanceof $J){ //note: '_elem instanceof this.$J' doesnt work. why??
+//            _elem = _elem.get(0);
+//        }
+//        if (typeof _elem.currentStyle != 'undefined'){
+//            computedStyle = _elem.currentStyle;
+//        }else{
+//            computedStyle = document.defaultView.getComputedStyle(_elem, null);
+//        }
+//        return computedStyle[_style];
+//    },
+//    //returns a hex color from strColor. strColor can be one of the predefined css colors (eg, 'aliceBlue', case insensitive)
+//    //or rgb (eg: rgb(12,0,45)) or an hex color (eg, '#ff98a3' or '#f8g'). Leading and trailing spaces will be omitted,
+//    //case is insensitive, an empty string is returned in case of error (bad format string, parseInt errors etcetera..)
+//    color : function(strColor){
+//        if(!strColor){
+//            return '';
+//        }
+//        strColor = strColor.replace(/(^\s+|\s+$)/g,'').toLowerCase();
+//        if(!strColor){
+//            return '';
+//        }
+//        var predefined_colors = {
+//            aliceblue:"#f0f8ff",
+//            antiquewhite:"#faebd7",
+//            aqua:"#00ffff",
+//            aquamarine:"#7fffd4",
+//            azure:"#f0ffff",
+//            beige:"#f5f5dc",
+//            bisque:"#ffe4c4",
+//            black:"#000000",
+//            blanchedalmond:"#ffebcd",
+//            blue:"#0000ff",
+//            blueviolet:"#8a2be2",
+//            brown:"#a52a2a",
+//            burlywood:"#deb887",
+//            cadetblue:"#5f9ea0",
+//            chartreuse:"#7fff00",
+//            chocolate:"#d2691e",
+//            coral:"#ff7f50",
+//            cornflowerblue:"#6495ed",
+//            cornsilk:"#fff8dc",
+//            crimson:"#dc143c",
+//            cyan:"#00ffff",
+//            darkblue:"#00008b",
+//            darkcyan:"#008b8b",
+//            darkgoldenrod:"#b8860b",
+//            darkgray:"#a9a9a9",
+//            darkgrey:"#a9a9a9",
+//            darkgreen:"#006400",
+//            darkkhaki:"#bdb76b",
+//            darkmagenta:"#8b008b",
+//            darkolivegreen:"#556b2f",
+//            darkorange:"#ff8c00",
+//            darkorchid:"#9932cc",
+//            darkred:"#8b0000",
+//            darksalmon:"#e9967a",
+//            darkseagreen:"#8fbc8f",
+//            darkslateblue:"#483d8b",
+//            darkslategray:"#2f4f4f",
+//            darkslategrey:"#2f4f4f",
+//            darkturquoise:"#00ced1",
+//            darkviolet:"#9400d3",
+//            deeppink:"#ff1493",
+//            deepskyblue:"#00bfff",
+//            dimgray:"#696969",
+//            dimgrey:"#696969",
+//            dodgerblue:"#1e90ff",
+//            firebrick:"#b22222",
+//            floralwhite:"#fffaf0",
+//            forestgreen:"#228b22",
+//            fuchsia:"#ff00ff",
+//            gainsboro:"#dcdcdc",
+//            ghostwhite:"#f8f8ff",
+//            gold:"#ffd700",
+//            goldenrod:"#daa520",
+//            gray:"#808080",
+//            grey:"#808080",
+//            green:"#008000",
+//            greenyellow:"#adff2f",
+//            honeydew:"#f0fff0",
+//            hotpink:"#ff69b4",
+//            indianred:"#cd5c5c",
+//            indigo:"#4b0082",
+//            ivory:"#fffff0",
+//            khaki:"#f0e68c",
+//            lavender:"#e6e6fa",
+//            lavenderblush:"#fff0f5",
+//            lawngreen:"#7cfc00",
+//            lemonchiffon:"#fffacd",
+//            lightblue:"#add8e6",
+//            lightcoral:"#f08080",
+//            lightcyan:"#e0ffff",
+//            lightgoldenrodyellow:"#fafad2",
+//            lightgray:"#d3d3d3",
+//            lightgrey:"#d3d3d3",
+//            lightgreen:"#90ee90",
+//            lightpink:"#ffb6c1",
+//            lightsalmon:"#ffa07a",
+//            lightseagreen:"#20b2aa",
+//            lightskyblue:"#87cefa",
+//            lightslategray:"#778899",
+//            lightslategrey:"#778899",
+//            lightsteelblue:"#b0c4de",
+//            lightyellow:"#ffffe0",
+//            lime:"#00ff00",
+//            limegreen:"#32cd32",
+//            linen:"#faf0e6",
+//            magenta:"#ff00ff",
+//            maroon:"#800000",
+//            mediumaquamarine:"#66cdaa",
+//            mediumblue:"#0000cd",
+//            mediumorchid:"#ba55d3",
+//            mediumpurple:"#9370d8",
+//            mediumseagreen:"#3cb371",
+//            mediumslateblue:"#7b68ee",
+//            mediumspringgreen:"#00fa9a",
+//            mediumturquoise:"#48d1cc",
+//            mediumvioletred:"#c71585",
+//            midnightblue:"#191970",
+//            mintcream:"#f5fffa",
+//            mistyrose:"#ffe4e1",
+//            moccasin:"#ffe4b5",
+//            navajowhite:"#ffdead",
+//            navy:"#000080",
+//            oldlace:"#fdf5e6",
+//            olive:"#808000",
+//            olivedrab:"#6b8e23",
+//            orange:"#ffa500",
+//            orangered:"#ff4500",
+//            orchid:"#da70d6",
+//            palegoldenrod:"#eee8aa",
+//            palegreen:"#98fb98",
+//            paleturquoise:"#afeeee",
+//            palevioletred:"#d87093",
+//            papayawhip:"#ffefd5",
+//            peachpuff:"#ffdab9",
+//            peru:"#cd853f",
+//            pink:"#ffc0cb",
+//            plum:"#dda0dd",
+//            powderblue:"#b0e0e6",
+//            purple:"#800080",
+//            red:"#ff0000",
+//            rosybrown:"#bc8f8f",
+//            royalblue:"#4169e1",
+//            saddlebrown:"#8b4513",
+//            salmon:"#fa8072",
+//            sandybrown:"#f4a460",
+//            seagreen:"#2e8b57",
+//            seashell:"#fff5ee",
+//            sienna:"#a0522d",
+//            silver:"#c0c0c0",
+//            skyblue:"#87ceeb",
+//            slateblue:"#6a5acd",
+//            slategray:"#708090",
+//            slategrey:"#708090",
+//            snow:"#fffafa",
+//            springgreen:"#00ff7f",
+//            steelblue:"#4682b4",
+//            tan:"#d2b48c",
+//            teal:"#008080",
+//            thistle:"#d8bfd8",
+//            tomato:"#ff6347",
+//            turquoise:"#40e0d0",
+//            violet:"#ee82ee",
+//            wheat:"#f5deb3",
+//            white:"#ffffff",
+//            whitesmoke:"#f5f5f5",
+//            yellow:"#ffff00",
+//            yellowgreen:"#9acd32"
+//        };
+//        var cl = predefined_colors[strColor];
+//        if(cl){
+//            return cl;
+//        }
+//        var pint = parseInt;
+//        //color parsers: note that the order is not random: we put first the most likely case ('#aaaaaa') and at last
+//        //the less one ('rgb(...)'), this might increase performances in most cases, especially if this method is called from
+//        //within a loop
+//        var color_defs = [
+//        {
+//            re: /^#*(\w{2})(\w{2})(\w{2})$/, //example: ['#00ff00', '336699'],
+//            process: function (bits){
+//                return [pint(bits[1], 16),pint(bits[2], 16),pint(bits[3], 16)];
+//            }
+//        },
+//
+//        {
+//            re: /^#*(\w{1})(\w{1})(\w{1})$/, //example: ['#fb0', 'f0f'],
+//            process: function (bits){
+//                return [pint(bits[1] + bits[1], 16),pint(bits[2] + bits[2], 16),pint(bits[3] + bits[3], 16)];
+//            }
+//        },
+//        {
+//            re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, //example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'],
+//            process: function (bits){
+//                return [pint(bits[1]), pint(bits[2]),pint(bits[3])];
+//            }
+//        }
+//
+//        ];
+//
+//        // search through the definitions to find a match
+//        var nan = isNaN;
+//        for (var i = 0; i < color_defs.length; i++) {
+//            var re = color_defs[i].re;
+//            var processor = color_defs[i].process;
+//            var bits = re.exec(strColor);
+//            if (bits) {
+//                var channels = processor(bits);
+//                if(channels.length ==3){
+//                    for(var j=0; j<3; j++){
+//                        var c = channels[j];
+//                        c = (nan(c) || c< 0 ? 0 : (c>255 ? 255 :c));
+//                        c = c.toString(16);
+//                        var len = c.length;
+//                        switch(len){
+//                            case 1:
+//                                c = '0'+c;
+//                                break;
+//                            case 2:
+//                                break;
+//                            default:
+//                                return '';
+//                        }
+//                        channels[j] = c;
+//                    }
+//                    return '#'+channels.join('');
+//                }
+//            }
+//        }
+//        return '';
+//    }
+//
 });
 
 /**
@@ -746,149 +749,64 @@ Timeside.classes.TimesideArray = Timeside.classes.TimesideClass.extend({
         }
         return me.splice(0,l);
     },
-    //moves the element from position [from] to position [to]. Shifts all elements
-    //from position [to] (inclusive) of one position. Note that the elemnt at position from is first removed
-    //and then inserted at position to. Therefore,
-    //if to==from+1 the element is not moved. Returns from if the element
-    //is not moved, i.e. either in the case above, or when:
-    //1) from or to are not integers or from or to are lower than zero or greater than the array length.
-    //in any other case, returns the index of the element moved, which is not necessarily to:
-    //It is, if to<from, otherwise (to>from+1) is to-1
+    //moves the element at position from into position to
+    //the element that was at from will be at position to
+    //returns:
+    //-1 if from or to not integers, or from or to not within the array bounds (lower than zero or greater or equal to this.length)
+    //from if from === to (no move)
+    //to otherwise (move succesful)
     move : function(from, to){
+
         var pInt = parseInt;
-        if(pInt(from)!==from || pInt(to)!==to){
+        if(pInt(from)!==from || pInt(to)!==to){ //just a check
+            return -1;
+        }
+        if(from === to){
             return from;
         }
         var me =this.toArray();
         var len = me.length;
-        if((from<0 || from>len)||(to<0 || to>len)){
-            return from;
-        }
-        //if we moved left to right, the insertion index is actually
-        //newIndex-1, as we must also consider the removal of the object at index from
-        if(to>from){
-            to--;
-        }
-        if(from != to){
-            var elm = me.splice(from,1)[0];
-            me.splice(to,0,elm);
+        if((from<0 || from>=len)||(to<0 || to>=len)){
+            return -1;
         }
+        var elm = me.splice(from,1)[0];
+        me.splice(to,0,elm);
+
         return to;
     }
+    //moves the element from position [from] to position [to]. Shifts all elements
+    //from position [to] (inclusive) of one position. Note that the elemnt at position from is first removed
+    //and then inserted at position to. Therefore,
+    //if to==from+1 the element is not moved. Returns from if the element
+    //is not moved, i.e. either in the case above, or when:
+    //1) from or to are not integers or from or to are lower than zero or greater than the array length.
+    //in any other case, returns the index of the element moved, which is not necessarily to:
+    //It is, if to<from, otherwise (to>from+1) is to-1
+//    move : function(from, to){
+//
+//        var pInt = parseInt;
+//        if(pInt(from)!==from || pInt(to)!==to){
+//            return from;
+//        }
+//        var me =this.toArray();
+//        var len = me.length;
+//        if((from<0 || from>len)||(to<0 || to>len)){
+//            return from;
+//        }
+//        //if we moved left to right, the insertion index is actually
+//        //newIndex-1, as we must also consider the removal of the object at index from
+//        if(to>from){
+//            to--;
+//        }
+//        if(from != to){
+//            var elm = me.splice(from,1)[0];
+//            me.splice(to,0,elm);
+//        }
+//        return to;
+//    }
 });
 
 
-/*
-* Sets a "tab look" on some elements of the page. Takes at least 3 arguments, at most 5:
-* 1st argument: an array (or a jquery object) of html elements, ususally anchors, representing the tabs
-* 2nd argument: an array (or a jquery object) of html elements, ususally divs, representing the containers to be shown/hidden when 
-*   clicking the tabs. The n-th tab will set the n-th container to visible, hiding the others. So order is important. Note that if tabs
-*   or container are jQuery objects, the html elements inside them are sorted according to the document order. That's why tabs and
-*   container can be passed also as javascript arrays, so that the binding n-th tab -> n-th container can be decided by the user
-*   regardeless on how elements are written on the page, if already present
-* 3rd argument: the selected index. If missing it defaults to zero.
-* 4th argument: selectedtab class. Applies to the selected tab after click of one tab. If missing, nothing is done
-* 5th argument the unselectedtab class. Applies to all tabs not selected after click of one tab. If missing, nothing is done
-*
-* NOTE: The last 2 arguments are mostly for customizing the tab "visual look", as some css elements (eg, (position, top, zIndex)
-* are set inside the code and cannot be changed, as they are mandatory to let tab anchor behave like desktop application tabs. Note also
-* that every tab container is set to 'visible' by means of jQuery.show()
-*
-* Examples:
-* setUpPlayerTabs([jQuery('#tab1),jQuery('#tab1)], [jQuery('#div1),jQuery('#div2)], 1);
-* sets the elements with id '#tab1' and '#tab2' as tab and assign the click events to them so that clicking tab_1 will show '#div_1'
-* (and hide '#div2') and viceversa for '#tab2'. The selected index will be 1 (second tab '#tab2')
-*/
-function setUpPlayerTabs() {//called from within controller.js once all markers have been loaded.
-    //this is because we need all divs to be visible to calculate size. selIndex is optional, it defaults to 0
-    //
-    
-    var $J = jQuery;
-    var tabs_ = arguments[0];
-    var divs_ = arguments[1]; //they might be ctually any content, div is a shoertand
-
-    //converting arguments to array: tabs
-    var tabs=[];
-    if(tabs_ instanceof $J){
-        tabs_.each(function(i,elm){
-            tabs.push(elm);
-        });
-    }else{
-        tabs = tabs_;
-    }
-    //set the overflow property of the parent tab to visible, otherwise scrollbars are displayed
-    //and the trick of setting position:relative+top:1px+zIndices (see css) doesnt work)
-    $J(tabs).each(function(i,tab){
-        var t = $J(tab).attr('href','#');
-        t.show(); //might be hidden
-        //set necessary style for the tab appearence:
-        var overflow = t.parent().css('overflow');
-        if(overflow && overflow != 'visible'){
-            t.parent().css('overflow','visible');
-        }
-    });
-    //converting arguments to array: divs
-    var divs=[];
-    if(divs_ instanceof $J){
-        divs_.each(function(i,elm){
-            divs.push(elm);
-        });
-    }else{
-        divs = divs_;
-    }
-    
-    //reading remaing arguments (if any)
-    var selIndex = arguments.length>2 ? arguments[2] : 0;
-    var selectedTabClass = arguments.length>3 ? arguments[3] : undefined;
-    var unselectedTabClass = arguments.length>4 ? arguments[4] : undefined;
-
-    //function to be associate to every click on the tab (see below)
-    var tabClicked = function(index) {
-        for(var i=0; i<tabs.length; i++){
-            var t = $J(tabs[i]);
-            
-            var div = $J(divs[i]);
-            var addClass = i==index ? selectedTabClass : unselectedTabClass;
-            var removeClass = i==index ? unselectedTabClass : selectedTabClass;
-            if(removeClass){
-                t.removeClass(removeClass);
-            }
-            if(addClass){
-                t.addClass(addClass);
-            }
-            
-            //relevant css. Will override any css set in stylesheets
-            t.css({
-                'position':'relative',
-                'top':'1px',
-                'zIndex': (i==index ? '10' : '0')
-            });
-            
-            if(i===index){
-                div.fadeIn('slow');
-            }else{
-                div.hide();
-            }
-        }
-    };
-    
-    //bind clicks on tabs to the function just created
-    for (var i=0;i<tabs.length;i++){
-        // introduce a new scope (round brackets)
-        //otherwise i is retrieved from the current scope and will be always equal to tabs.length
-        //due to this loop
-        (function(tabIndex){
-            $J(tabs[i]).click(function(){
-                tabClicked(tabIndex);
-                return false;//returning false avoids scroll of the anchor to the top of the page
-            });
-        })(i);
-    }
-    
-    //select the tab
-    $(tabs[selIndex]).trigger("click");
-}
-
 /**
 * Loads scripts asynchronously
 * can take up to four arguments: