]> git.parisson.com Git - telemeta.git/commitdiff
restyle of the code. Still to be added: click on the image to set position and option...
authorriccardo <riccardo@parisson.com>
Tue, 26 Apr 2011 17:27:44 +0000 (19:27 +0200)
committerriccardo <riccardo@parisson.com>
Tue, 26 Apr 2011 17:27:44 +0000 (19:27 +0200)
13 files changed:
telemeta/htdocs/css/telemeta.css
telemeta/htdocs/images/transparent.png [new file with mode: 0644]
telemeta/htdocs/js/application.js
telemeta/htdocs/js/playerUtils.js
telemeta/htdocs/timeside/src/divmarker.js
telemeta/htdocs/timeside/src/markermap.js
telemeta/htdocs/timeside/src/player.js
telemeta/htdocs/timeside/src/ruler.js
telemeta/htdocs/timeside/src/rulermarker.js
telemeta/htdocs/timeside/src/timeside.js
telemeta/templates/telemeta_default/base.html
telemeta/templates/telemeta_default/mediaitem_detail.html
telemeta/urls.py

index 3ee4759a90972ca1ae2fd91ce310b5d041e535a3..49ed6ece157d4c283b6dee3b99468d5c4d4205cb 100644 (file)
@@ -862,14 +862,14 @@ a.image-link {
 }
 
 
-.tab_unselected {
+.tab_unselected, .tab_unselected:hover, .tab_unselected:visited {
     background-color: #cccccc;
     font-weight: normal;
     color: #333333;
     border: 1px solid #cccccc;
-    z-index: 0;
+   
 }
-.tab_selected {
+.tab_selected, .tab_selected:hover, .tab_selected:visited {
     background-color: #ffffff;
     color: #000000;
     font-weight: bold;
@@ -877,9 +877,16 @@ a.image-link {
     border-right: 1px solid #999999;
     border-left: 1px solid #999999;
     border-bottom: 1px solid #ffffff;
-    z-index: 10;
+   
 }
 
+.tab, .tab:hover, .tab:visited{
+    margin-top: 1ex;
+    display: inline-block;
+    margin-left: 0px;
+    padding: 1ex;
+    -moz-border-radius: 1ex 1ex 0ex 0ex;  -webkit-border-radius: 1ex 1ex 0ex 0ex;  border-radius: 1ex 1ex 0ex 0ex;
+}
 
 .roundBorder4{
     /*padding: 0.3em 0.8em 0.8em 0.8em;
@@ -918,103 +925,61 @@ a.image-link {
     border: 0px !important;
 }
 
-.markersdivIndexLabel{
-    color: #fff;
-    background-image:url("/images/marker_tiny.png");
-    background-repeat:no-repeat;
-    background-position:center center;
-    font-size: 90%;
-    font-weight:bold;
-    display:inline-block;
-    width:3ex;
-    text-align: center;
-    font-family: monospace;
-    margin-right:2ex;
+.markerdiv div{
+    padding:1ex;
 }
-.markersdivIndexLabel span{
-    position:relative;
-    top:-2px;
+.markerdiv div[zero_top_padding]{ /*mathces any div with the attribute zero_top_padding set (whatever the value)*/
+    padding-top:0px;
 }
-.markersdivOffset{
-    margin-right:1ex;
-    font-style: italic;
+.markerdiv div *{
+    vertical-align:middle;
+    font-family: sans-serif;
 }
-.markersdivTopElement{
-   /* vertical-align:middle;*/
+.markerdiv div input, .markerdiv div textarea{
+    margin:0px;
+    padding:2px;
 }
-.markersdivTitle{
-    font-weight:bold;
-    margin:0;
-    padding:0;
-    position:relative;
-    top:-0.5ex;
-    /*margin-right:margin*/
-}
-.markersdivEdit, .markersdivEdit:hover, .markersdivEdit:visited,
-.markersdivEdit:link, .markersdivEdit:link:hover, .markersdivEdit:visited:hover{
-    font-weight:bold;
-    float:right;
-    color:#333;
-    background-image: url('/images/edit_marker.png');
-    background-repeat:no-repeat;
-    background-position: center left;
-    border:2px solid #666;
-    height:2.2ex;
-    width: 8.5ex;
-    /*padding:0px 1ex 2ex 3ex;*/ /*top right bottom left*/
-    font-size: 65%;
-    margin-left: 1ex;
-    margin-right:1ex;
-    position:relative;
-    /*top:-0.5ex;*/
+.markerdiv .ts-marker{
+    background-image: url('/images/marker_tiny.png'); text-align: center; min-width:3ex;
 }
-.markersdivEdit span{
-    position:absolute;top:-0.75ex;left:3ex;
-}
-.markersdivDelete, .markersdivAddPlaylist{
-    border:0px;
-    float:right;
-    background-image: url('/images/del_marker.png');
+.markerdiv .ts-marker, .markerdiv .markersdivOffset, .markerdiv .markersdivTitle, .markerdiv .markersdivAddPlaylist, .markerdiv .markersdivEdit{margin-right:.8ex;}
+.markerdiv div a, .markerdiv div a:visited, .markerdiv div a:hover{
+    display: inline-block; /*otherwise width and height do not work*/
     background-repeat: no-repeat;
-    background-position: top center;
-    /*padding:1ex 1ex 1ex 1ex; top right bottom left. The padding is only to show the element */
-    width: 15px;
-    height:15px;
-}
-.markersdivDelete{
-    background-image: url('/images/del_marker.png');
-}
-.markersdivAddPlaylist{
-    background-image: url('/images/add_playlist_marker.png');
+    text-decoration: none;
 }
-.markersdivDescription{
-    margin:0ex 0px 0ex 0px; /*top right bottom left*/
-    font-family: sans-serif;
-    padding:0px;
-    width:100%;
+.markerdiv .ts-marker, .markerdiv .ts-marker:hover, .markerdiv .ts-marker:visited{
+    font-family: monospace; background: #e65911;color: #FFF;padding-left: .3ex; padding-right:.3ex;
+}
+.markersdivDelete{    background-image: url('/images/del_marker.png');width:15px;height:2ex;}
+.markersdivAddPlaylist{    background-image: url('/images/add_playlist_marker.png');width:13px;height:2ex; }
+.markersdivTitle{    font-weight:bold;}
+.markersdivEdit, .markersdivEdit:hover, .markersdivEdit:visited{
+    line-height: normal;
+    color:#000;
+     background-position: -2px center;
+    padding-left: 13px; padding-right: 2px;
+    font-size: 65%;
+    border:2px solid #666;
+    background-color: #fff;
+    background-image: url('/images/edit_marker.png');
+    -moz-border-radius: 1ex; -webkit-border-radius: 1ex; border-radius: 1ex;
 }
-.markersdivSave, .markersdivSave:hover, .markersdivSave:visited,
-.markersdivSave:link, .markersdivSave:link:hover, .markersdivSave:visited:hover{
+.markersdivSave, .markersdivSave:hover, .markersdivSave:visited{
     background-color: #087714;
     color: #fff;
     font-weight: bold;
-    /*padding: 0.3em 0.8em 0.3em 0.8em;
-    padding:5px 10px 5px 26px;*/
-    display:block;
-    width: 4ex;
-    padding: 1ex .5ex 1ex 3.5ex;
-    background-repeat: no-repeat;
-    background-position: 1ex 1ex;
-    -moz-border-radius: 1ex 1ex 1ex 1ex;
-    -webkit-border-radius: 1ex 1ex 1ex 1ex;
-    border-radius: 1ex 1ex 1ex 1ex;
+    padding:.7ex; padding-left: 20px;
+    background-position: 5px center;
+    -moz-border-radius: 1ex;-webkit-border-radius: 1ex;border-radius: 1ex;
     background-image: url('/images/ok.png');
 }
-
-.markerdiv{
-    padding:1ex 1ex 1ex 1ex;  /*top right bottom left*/
+.markerdiv{ 
     border: 1px solid #aaaaaa;
     margin-bottom: 1ex;
+    -moz-border-radius: 1e;
+    -webkit-border-radius: 1ex;
+    border-radius: 1ex;
 }
 
 /*----------------------------------*/
diff --git a/telemeta/htdocs/images/transparent.png b/telemeta/htdocs/images/transparent.png
new file mode 100644 (file)
index 0000000..e1ab124
Binary files /dev/null and b/telemeta/htdocs/images/transparent.png differ
index fbb40187505535c660ef2454f6f34c2da8ddbf61..f42724762627dac526270109cec32ae17d227d1d 100644 (file)
 //2) from==to or from==to-1 (also in this latter case there is no need to move)\r
 //3) from or to are lower than zero or greater than the array length\r
 //in any other case, returns to\r
-Array.prototype.move = function(from, to){\r
-    var pInt = parseInt;\r
-    if(pInt(from)!==from || pInt(to)!==to){\r
-        return from;\r
-    }\r
-    var len = this.length;\r
-    if((from<0 || from>len)||(to<0 || to>len)){\r
-        return from;\r
-    }\r
-    //if we moved left to right, the insertion index is actually\r
-    //newIndex-1, as we must also consider to remove the current index markerIndex, so:\r
-    if(to>from){\r
-        to--;\r
-    }\r
-    if(from != to){\r
-        var elm = this.splice(from,1)[0];\r
-        this.splice(to,0,elm);\r
-        return to;\r
-    }\r
-    return from;\r
-}\r
+//Array.prototype.move = function(from, to){\r
+//    var pInt = parseInt;\r
+//    if(pInt(from)!==from || pInt(to)!==to){\r
+//        return from;\r
+//    }\r
+//    var len = this.length;\r
+//    if((from<0 || from>len)||(to<0 || to>len)){\r
+//        return from;\r
+//    }\r
+//    //if we moved left to right, the insertion index is actually\r
+//    //newIndex-1, as we must also consider to remove the current index markerIndex, so:\r
+//    if(to>from){\r
+//        to--;\r
+//    }\r
+//    if(from != to){\r
+//        var elm = this.splice(from,1)[0];\r
+//        this.splice(to,0,elm);\r
+//        return to;\r
+//    }\r
+//    return from;\r
+//}\r
 \r
 function foldInfoBlocks() {\r
     var $J = jQuery;\r
@@ -50,7 +50,9 @@ function urlNormalized(){
     return sPath;\r
 }\r
 \r
-\r
+/**\r
+ * Global telemeta function to set the current selected menu active according toi the current url\r
+ */\r
 function setSelectedMenu(){\r
     var $J = jQuery;\r
     var menus = $J('#menu a');\r
@@ -87,7 +89,7 @@ function setSelectedMenu(){
             }\r
         }else{\r
             //here, on the other hand, we select if a link points to a page or super page\r
-            //of the current paqge\r
+            //of the current page\r
             if(linkHref!=pageOrigin && pageHref.match("^"+linkHref+".*")){\r
                 elm.addClass('active');\r
             }else{\r
@@ -116,14 +118,14 @@ $(document).ready(function() {
 //method: the json method, eg "telemeta.update_marker". See base.py\r
 //\r
 //onSuccesFcn(data, textStatus, jqXHR) OPTIONAL --IF MISSING, NOTHING HAPPENS --\r
-//   A function to be called if the request succeeds.\r
-//   The function gets passed three arguments:\r
+//   A function to be called if the request succeeds with the same syntax of jQuery's ajax onSuccess function.\r
+//   The function gets passed three arguments \r
 //      The data returned from the server, formatted according to the dataType parameter;\r
 //      a string describing the status;\r
 //      and the jqXHR (in jQuery 1.4.x, XMLHttpRequest) object\r
 //\r
 //onErrorFcn(jqXHR, textStatus, errorThrown) OPTIONAL. --IF MISSING, THE DEFAULT ERROR DIALOG IS SHOWN--\r
-//    A function to be called if the request fails.\r
+//    A function to be called if the request fails with the same syntax of jQuery ajax onError function..\r
 //    The function receives three arguments:\r
 //      The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object,\r
 //      a string describing the type of error that occurred and\r
@@ -227,20 +229,6 @@ var popup={
             return div;\r
         },\r
         className: 'component',\r
-//        divShadow: function(){\r
-//            var divShadow =  this.jQuery('<div/>').css({ //this is _cfg_\r
-//                position: 'absolute',\r
-//                display: 'none',\r
-//                overflow:'visible',\r
-//                padding: '0 !important', //otherwise setting divShadow dimension is tricky\r
-//                backgroundColor:'#000 !important', //shadow must be black\r
-//                zIndex:999\r
-//            });\r
-//            if(this.className){\r
-//                divShadow.addClass(this.className);\r
-//            }\r
-//            return divShadow;\r
-//        },\r
         //        mouseDownNamespace : "mousedown.popup__",\r
         //        keyDownNamespace : "keydown.popup__",\r
 \r
@@ -528,45 +516,78 @@ var popup={
 \r
 }\r
 \r
-\r
-//function loadScripts(scriptArrayName, callback){\r
-//     if(!(scriptArrayName) && callback){\r
-//         callback();\r
-//         return;\r
-//     }\r
-//     var len = scriptArrayName.length;\r
-//     var script = function(i){\r
-//         if(i>=len){\r
-//             if(callback){\r
-//                callback();\r
-//                return;\r
-//             }\r
-//         }\r
-//         jQuery.getScript(scriptArrayName[i], function(){script(i+1)});\r
-//     };\r
-//     script(0);\r
-//}\r
-\r
-function loadScripts(scriptArray, callback, loadInSeries){\r
-    loadInSeries = false;\r
+/**\r
+ * Loads scripts asynchronously\r
+ * can take up to four arguments:\r
+ * root (optional): a string specifying the root (such as '/usr/local/'). Must end with slash, otherwise\r
+ *      each element in scriptArray must begin with a slash\r
+ * scriptArray: a string array of js script filenames, such as ['script1.js','script2.js']\r
+ * callback (optional): callback to be executed when ALL scripts are succesfully loaded\r
+ * loadInSeries (optional): if true scripts are loaded in synchronously, ie each script is loaded only once the\r
+ *      previous has been loaded. The default (argument missing) is false\r
+ *\r
+ * Examples. Given scripts = ['s1.js', 's2.js']\r
+ *  loadScripts(scripts)                          //loads (asynchronously) scripts\r
+ *  loadScripts('/usr/', scripts)                 //loads (asynchronously) ['/usr/s1.js', '/usr/s2.js']\r
+ *  loadScripts(scripts, callback)                //loads (asynchronously) scripts. When loaded, executes callback\r
+ *  loadScripts('/usr/', scripts, callback)       //loads (asynchronously) ['/usr/s1.js', '/usr/s2.js']. When loaded, executes callback\r
+ *  loadScripts(scripts, callback, true)          //loads (synchronously) scripts. When loaded, executes callback\r
+ *  loadScripts('/usr/', scripts, callback, true) //loads (synchronously) ['/usr/s1.js', '/usr/s2.js']. When loaded, executes callback\r
+ *\r
+ */\r
+function loadScripts(){\r
+    var optionalRoot='', scriptArray=[], callback=undefined, loadInSeries=false;\r
+    var len = arguments.length;\r
+    if(len==1){\r
+        scriptArray = arguments[0];\r
+    }else if(len==2){\r
+        if(typeof arguments[0] == 'string'){\r
+            optionalRoot = arguments[0];\r
+            scriptArray = arguments[1];\r
+        }else{\r
+            scriptArray = arguments[0];\r
+            callback = arguments[1];\r
+        }\r
+    }else if(len>2){\r
+        if(typeof arguments[0] == 'string'){\r
+            optionalRoot = arguments[0];\r
+            scriptArray = arguments[1];\r
+            callback = arguments[2];\r
+            if(len>3){\r
+                loadInSeries = arguments[3];\r
+            }\r
+        }else{\r
+            scriptArray = arguments[0];\r
+            callback = arguments[1];\r
+            loadInSeries = arguments[2];\r
+        }\r
+    }\r
+    \r
     if(!scriptArray){\r
         if(callback){\r
             callback();\r
         }\r
         return;\r
     }\r
-    var len = scriptArray.length;\r
+    len = scriptArray.length;\r
+    var i=0;\r
+    if(optionalRoot){\r
+        for(i =0; i<len; i++){\r
+            scriptArray[i] = optionalRoot+scriptArray[i];\r
+        }\r
+    }\r
+\r
     var $J = jQuery;\r
-    var time = new Date().getTime();\r
+    //var time = new Date().getTime();\r
     if(loadInSeries){\r
-        var load = function(i){\r
-            if(i<len){\r
-                consolelog("loading "+scriptArray[i]+" "+new Date().getTime());\r
-                $J.getScript(scriptArray[i],function(){\r
-                    load(i+1);\r
+        var load = function(index){\r
+            if(index<len){\r
+                //consolelog("loading "+scriptArray[index]+" "+new Date().getTime());\r
+                $J.getScript(scriptArray[index],function(){\r
+                    load(index+1);\r
                 });\r
             }else if(callback){\r
-                consolelog("EXECUTING CALLBACK ELAPSED TIME:"+(new Date().getTime()-time));\r
+                //consolelog("EXECUTING CALLBACK ELAPSED TIME:"+(new Date().getTime()-time));\r
                 callback();\r
             }\r
         };\r
@@ -574,13 +595,13 @@ function loadScripts(scriptArray, callback, loadInSeries){
     }else{\r
         var count=0;\r
         var s;\r
-        for(var i=0; i <len; i++){\r
+        for(i=0; i <len; i++){\r
             s = scriptArray[i];\r
-            consolelog("loading "+s+" "+new Date().getTime());\r
+            //consolelog("loading "+s+" "+new Date().getTime());\r
             $J.getScript(s, function(){\r
                 count++;\r
                 if(count==len && callback){\r
-                    consolelog("EXECUTING CALLBACK ELAPSED TIME:"+(new Date().getTime()-time));\r
+                    //consolelog("EXECUTING CALLBACK ELAPSED TIME:"+(new Date().getTime()-time));\r
                     callback();\r
                 }\r
             });\r
index 2cf4b632fc2d8ec021f38646f1e2ba8ebb035e87..5416f712406e63b1c6b69559910215c04e4c46de 100644 (file)
@@ -1,11 +1,4 @@
-//var sound = null;
-//var soundUrl = null;
-//var soundEngineReady = false;
-var map;
-//var provider;
-var player;
-var player_image_url = null;
-var controller;
+var player; //public player variable
 
 function togglePlayerMaximization() {
     consolelog('entered togglePlayerMaximization');
@@ -45,7 +38,9 @@ function change_visualizer_clicked(){
 
     //form.append(img);
     setTimeout(function(){
-        change_visualizer();
+        if (player){
+            player.refreshImage();
+        }
         //img.remove();
         setTimeout(function(){
             if(src){
@@ -56,19 +51,10 @@ function change_visualizer_clicked(){
     },600);
 }
 
-function change_visualizer() {
-    consolelog('playerUtils.changeVisualizer');
-    set_player_image_url($('#visualizer_id').get(0).value);
-    if (player){
-        player.refreshImage();
-    }
-    return false;
-}
-
-
-
-
 function loadPlayer(analizerUrl, soundUrl){
+    if(!(analizerUrl) || !(soundUrl)){
+        return;
+    }
     var $J = jQuery;
     var msgElm = $J('#loading_span_text'); //element to show messages
     if(msgElm){
@@ -86,20 +72,20 @@ function loadPlayer(analizerUrl, soundUrl){
                 var elm = $J(element);
                 tableBody.append('<tr><td>'+elm.attr('name')+'</td><td>'+elm.attr('value')+'</td><td>'
                     +elm.attr('unit')+'</td></tr>');
-                });
-                //loaded analizer, loading player
-                if(msgElm){
-                    msgElm.html('Loading player...');
-                }
-                var duration = $J(data).find('#duration').attr('value');
-                duration = duration.split(":");
-                consolelog('analyzer loaded, duration: '+duration);
-                //format duration
-                var pin = parseInt;
-                var pfl = parseFloat;
-                var timeInMSecs=pin(duration[0])*3600+pin(duration[1])*60+pfl(duration[2]);
-                timeInMSecs = Math.round(timeInMSecs*1000);
-                load_player(soundUrl, timeInMSecs);
+            });
+            //loaded analizer, loading player
+            if(msgElm){
+                msgElm.html('Loading player...');
+            }
+            var duration = $J(data).find('#duration').attr('value');
+            duration = duration.split(":");
+            consolelog('analyzer loaded, duration: '+duration);
+            //format duration
+            var pin = parseInt;
+            var pfl = parseFloat;
+            var timeInMSecs=pin(duration[0])*3600+pin(duration[1])*60+pfl(duration[2]);
+            timeInMSecs = Math.round(timeInMSecs*1000);
+            load_player(soundUrl, timeInMSecs);
         },
         error:function(){
             msgElm.parent().html("<img src='/images/dialog-error.png' style='vertical-align:middle'/><span class='login-error'>Error loading analyzer</span>");
@@ -110,9 +96,6 @@ function loadPlayer(analizerUrl, soundUrl){
 
 //loads a player WAITING for the sound identified by soundUrl to be FULLY LOADED!!!!
 function load_player(soundUrl, durationInMsecs) {
-//    if (!$('#player').length){
-//        return;
-//    }
     consolelog('PlayerUtils.load_player: '+soundUrl+' '+durationInMsecs);
     var load_player2 = this.load_player2;
 
@@ -136,33 +119,31 @@ function load_player(soundUrl, durationInMsecs) {
         }
     });
     if(!loadImmediately){
-        load_player2(sound,durationInMsecs);
+        //TODO: remove this code is only temporary here!!!!!!!!!!!!!!!!!!!!1
+        loadScripts('/timeside/src/',['rulermarker.js', //'markerlist.js',
+            'markermap.js', 'player.js', 'ruler.js','divmarker.js'], function(){
+                load_player2(sound,durationInMsecs);
+            });
     }
+
 }
-//NOTE: the sound MUST be FULLY LOADED!!!!!! otherwise the duration is null. This method is called from load_player
+//NOTE: the duration must be present. Loaded from xmlanalyzer (see above)
 function load_player2(sound, durationInMsec) {
     if (!$('#player').length){
         return;
     }
     consolelog("entered load_player2");
 
+    //TODO: what are we doing here????
     $('.ts-wave a img').insertAfter('.ts-wave a');
     $('.ts-wave a').remove();
 
-    TimeSide.load(function() {
-        map = new TimeSide.MarkerMap();
+    var p = new Player(jQuery('#player'), sound, durationInMsec);
+    consolelog('initialized player');
+    p._setupInterface(CURRENT_USER_NAME ? true : false);
+    //p.loadMarkers();
 
-        player = new TimeSide.Player('#player', {
-            image: get_player_image_src,
-            'sound': sound,
-            'soundDurationInMsec': durationInMsec
-        });
-        change_visualizer();
-        controller = new TimeSide.Controller({
-            player: player,
-            map: map
-        });
-    });
+    player = p;
 
     var change_visualizer_click = change_visualizer_clicked;
     $('#visualizer_id').change(change_visualizer_click);
@@ -176,98 +157,3 @@ function load_player2(sound, durationInMsec) {
 
 
 }
-function set_player_image_url(str) {
-    player_image_url = str;
-}
-
-function get_player_image_src(width, height) {
-    var src = null;
-    if (player_image_url && (width || height)) {
-        src = player_image_url.replace('WIDTH', width + '').replace('HEIGHT', height + '');
-    }
-    return src;
-}
-
-//======================================================================
-//OLD STUFF
-//======================================================================
-
-
-//TODO REMOVE NOTE: the sound MUST be FULLY LOADED!!!!!! otherwise the duration is null. This method is called from load_player
-function load_player2Old(sound) {
-    if (!$('#player').length){
-        return;
-    }
-    consolelog("entered load_player2");
-    //        sound = soundManager.createSound({
-    //            id: 'sound',
-    //            url: soundUrl
-    //        });
-    //soundUrl = $('.ts-wave a').attr('href');
-
-    $('.ts-wave a img').insertAfter('.ts-wave a');
-    $('.ts-wave a').remove();
-   
-    TimeSide.load(function() {
-        map = new TimeSide.MarkerMap();
-        //        provider = new TimeSide.SoundProvider({
-        //            duration: sound.duration
-        //        });
-        //        provider.setSource(sound);
-        player = new TimeSide.Player('#player', {
-            image: get_player_image_src,
-            'sound': sound
-        });
-        change_visualizer();
-        controller = new TimeSide.Controller({
-            player: player,
-            //soundProvider: provider,
-            map: map
-        });
-    //change_visualizer();
-    //player.resize();
-    //player.updateVolumeAnchor(provider.getVolume());
-    });
-
-    //    $('#visualizer_id').change(change_visualizer);
-    //    $('#visualizer_id_form').submit(change_visualizer);
-    $('#visualizer_id').change(change_visualizer_clicked);
-    $('#visualizer_id_form').submit(change_visualizer_clicked);
-
-    $('#player_maximized .toggle, #player_minimized .toggle').click(function() {
-        togglePlayerMaximization();
-        this.blur();
-        return false;
-    });
-
-
-}
-
-//TODO: REMOVE loads a player WAITING for the sound identified by soundUrl to be FULLY LOADED!!!!
-function load_playerOld(soundUrl) {
-    if (!$('#player').length){
-        return;
-    }
-    consolelog('PlayerUtils.load_player: '+soundUrl);
-    var callback = this.load_player2;
-
-    //this variable can be changed to load a sound immediately or not
-    var loadImmediately = true;
-    var sound = soundManager.createSound({
-        id: 'sound',
-        autoLoad: loadImmediately,
-        url: soundUrl,
-        whileloading: function() {
-            //PROBLEM/BUG: on chrome and firefox whileloading is the same as onload,
-            //ie it is not called at regular interval but when the whole file has loaded
-            if(loadImmediately){
-                consolelog('entering while loading setting up---------------'+this.bytesLoaded+' of '+this.bytesTotal);
-                loadImmediately=false;
-                callback(this);
-            }
-        }
-    });
-    if(!loadImmediately){
-        callback(sound);
-    }
-}
index 86cea4193ddc226296490c32cedc16cf60083d50..98e293d0383be55a3f7ae2faf5d69c5460653ef8 100644 (file)
 /**
  * TimeSide - Web Audio Components
- * Copyright (c) 2008-2009 Samalyse
+ * Copyright (c) 2011 Parisson
  * Author: Riccardo Zaccarelli
  * License: GNU General Public License version 2.0
  */
 
-TimeSide(function($N, $J) {
-
-    $N.Class.create("DivMarker", $N.Core, {
-        //static constant variables to retireve the Marker Html Elements (MHE)
-        //to be used with the function below getHtmElm, eg:
-        //getHtmElm(marker, this.MHE_OFFSET_LABEL)
-        e_indexLabel:null,
-        e_descriptionText:null,
-        e_offsetLabel:null,
-        e_deleteButton:null,
-        e_okButton:null,
-        e_header:null,
-        e_editButton:null,
-        e_titleText:null,
-        e_addplaylistButton:null,
-        me:null,
-        markerMap:null,
-
-
-        initialize: function($super, markermap) {
-            $super();
-            //sets the fields required???? see ruler.js createPointer
-            this.configure({
-                //why instantiating a variable to null?
-                parent: [null, 'required']
-            });
-            this.cfg.parent = $J("#markers_div_id");
-            this.markerMap = markermap;
-            this.me = this.createDiv();
-            this.cfg.parent.append(this.me);
-        },
-
-
-        //creates a new div. By default, text is hidden and edit button is visible
-        createDiv: function(){
-            
-            var div = this.cfg.parent;
-            var markerDiv;
-            if(div){
-                
-                //index label
-                this.e_indexLabel = $J('<span/>')
-                .addClass('markersdivIndexLabel')
-                .addClass('markersdivTopElement');
-
-                //offset label
-                this.e_offsetLabel = $J('<span/>')
-                .addClass('markersdivTopElement')
-                .addClass('markersdivOffset');
-                
-
-                //title text
-                this.e_titleText = $J('<input/>')
-                .attr('type','text')
-                .addClass('markersdivTitle')
-                .addClass('markersdivTopElement');
-                
-                //add playlist button
-                this.e_addplaylistButton = $J('<a/>')
-                .addClass('markersdivAddPlaylist')
-                .addClass('markersdivTopElement')
-                .attr('title','add to playlist')
-                .attr("href","#");
-                
-                //close button
-                this.e_deleteButton = $J('<a/>')
-                .addClass('markersdivDelete')
-                .addClass('markersdivTopElement')
-                .attr('title','delete marker')
-                .attr("href","#");
-               
-                //edit button
-                this.e_editButton = $J('<a/>')
-                .addClass('roundBorder4')
-                .addClass('markersdivEdit')
-                .addClass('markersdivTopElement')
-                .attr('title','edit marker description')
-                .attr("href","#")
-                .html('<span>EDIT</span>');
-                                
-                //add all elements to header:
-                this.e_header = $J('<div/>') //.css('margin','0.1ex 0ex 0.5ex 0ex')
-                .append(this.e_indexLabel)
-                .append(this.e_offsetLabel)
-                .append(this.e_titleText)
-                .append(this.e_deleteButton)
-                .append(this.e_editButton)
-                .append(this.e_addplaylistButton);
-                
-                //description text
-                this.e_descriptionText = $J('<textarea/>')
-                .addClass('markersdivDescription')
-
-                //ok button
-                this.e_okButton = $J('<a/>')
-                .attr('title','save marker description and offset')
-                .addClass('markersdivSave')
-                .attr("href","#")
-                .html("OK");
-                
-                //create marker div and append all elements
-                markerDiv = $J('<div/>')
-                .append(this.e_header)
-                .append(this.e_descriptionText)
-                .attr('tabIndex',0)
-                //.append(this.e_okButton)
-                .append($J('<div/>') //.css('margin','1ex 0ex 0.1ex 0ex')
-                .append(this.e_okButton))
-                .addClass('roundBorder8')
-                .addClass('markerdiv');
-                //set default visibility
-
-            }
-            return markerDiv;
-        },
-
-        updateMarkerIndex: function(index){
-            var map = this.markerMap;
-            var marker = map.get(index);
-
-            //set defualt element values regardeless of the marker state
-            this.e_indexLabel.attr('title',marker.toString());
-            this.e_indexLabel.html("<span>"+(index+1)+"</span>");
-            this.e_offsetLabel.html(this.formatMarkerOffset(marker.offset));
-            //move the div to the correct index
-            var divIndex = this.me.prevAll().length;
-            //move the div if necessary:
-            //note that moving left to right the actual insertionIndex is index-1
-            //because we will first remove the current index
-            var insertionIndex = index>divIndex ? index-1 : index;
-            if(insertionIndex!=divIndex){
-                this.me.detach();//The .detach() method is the same as .remove(), except that .detach() keeps
-                //all jQuery data associated with the removed elements
-                $( this.cfg.parent.children()[insertionIndex] ).before(this.me); //add
-            }
-            
-            //set visibility and attach events according to the marker state:
-            //first, is editing or not
-            var isEditing = marker.isEditable && marker.isModified;
-            //            (!marker.isSavedOnServer || !(this.e_editButton.is(':visible')));
-
-            if(!isEditing){
-                this.e_descriptionText.val(marker.desc ? marker.desc : "");
-                this.e_titleText.val(marker.title ? marker.title : "");
-            }
-
-            this.e_okButton.hide();
-            this.e_editButton.show();
-            this.e_deleteButton.show();
-            this.e_addplaylistButton.show();
-            this.e_descriptionText.attr('readonly','readonly').addClass('markersdivUneditable');
-            this.e_titleText.attr('readonly','readonly').addClass('markersdivUneditable');
-            
-
-            if(!marker.isEditable){
-                this.e_editButton.hide();
-                this.e_deleteButton.hide();
-                //we unbind events to be sure
-                this.e_addplaylistButton.unbind('click').hide();
-                this.e_okButton.unbind('click')
-                this.e_deleteButton.unbind('click').hide();
-                this.e_editButton.unbind('click').hide();
-                return false;
+var MarkerMapDiv = TimesideArray.extend({
+    init:function(){
+        this._super();
+        this.div = this.$J("#markers_div_id");
+    },
+    //overridden
+    add: function(marker, index,  isNew){
+         
+        var div = this.createMarkerDiv(index, marker);
+        if(index==this.length){
+                this.div.append(div);
+            }else{
+                this.$J( this.div.children()[index] ).before(div);
             }
-
-           
-            var remove = map.remove;
-            this.e_deleteButton.unbind('click').click( function(){
-                if(!(marker.isSavedOnServer) || confirm('delete the marker permanently?')){
-                    remove.apply(map,[index]);
-                }
-                return false; //avoid scrolling of the page on anchor click
-            })
-
-            this.e_addplaylistButton.unbind('click').bind('click',function(evtObj_){
-                playlistUtils.showPopupAddToPlaylist(evtObj_,'marker',""+marker.id,'marker added to selected playlist');return false;
-            });
-            //notifies controller.js
-            //                this.fire('remove', {
-            //                    index: index
-            //                });
-
-            var dText = this.e_descriptionText;
-            var tText  = this.e_titleText;
-            var okB = this.e_okButton;
-            var utw = this.updateTitleWidth;
-            var divmarker = this;
-            var eB = this.e_editButton;
-            var startEdit = function(){
-                marker.isModified = true;
-                dText.removeAttr('readonly').removeClass('markersdivUneditable').show();
-                tText.removeAttr('readonly').removeClass('markersdivUneditable').show();
-                okB.show();
-                eB.hide();
-                utw.apply(divmarker,[tText]);
-            };
-
-//            dText.unbind('focus').focus(function(){this.debug('dText focus')});
-//            tText.unbind('focus').focus(function(){this.debug('tText focus')});
-//            dText.unbind('blur').blur(function(){this.debug('dText blur')});
-//            tText.unbind('blur').blur(function(){this.debug('tText blur')});
-
-
-            this.e_editButton.unbind('click').click( function(){
-                startEdit();
-                divmarker.focusOn();
-                return false; //avoid scrolling of the page on anchor click
-            });
-            
-            //action for ok button
-            this.e_okButton.unbind('click').click( function(){
-                //if(marker.desc !== descriptionText.val()){ //strict equality needed. See note below
-                marker.desc = dText.val();
-                marker.title = tText.val();
-                map.sendHTTP(marker,
-                    
-                    function(){
-                        dText.attr('readonly','readonly').addClass('markersdivUneditable');
-                        tText.attr('readonly','readonly').addClass('markersdivUneditable');
-                        eB.show();
-                        okB.hide();
-                        utw.apply(divmarker,[tText]);
-                    },
-                    true
-                    );
-                return false; //avoid scrolling of the page on anchor click
+        //this.setIndex(this.length-1,d); //length has been increased when calling super
+        this._super(div,index);
+        if(isNew){
+            this.setEditMode(index,true);
+            this.setFocus(index,true);
+        }
+        if(index<this.length){
+            //update indices. Note that this is NOT done at startup as index == this.length ALWAYS
+            var t = this;
+            var setIdx = t.setIndex;
+            this.each(index, function(i, div){
+                setIdx.apply(t,[div,i]);
             });
-            
-            if(isEditing){
-                startEdit();
+        }
+        this.stretch(div.find('.markersdivTitle'));
+        this.stretch(div.find('.markersdivDescription'));
+        return div;
+    },
+    //overridden
+    move: function(from, to, newOffset){
+
+        //call super method
+        var realIndex = this._super(from,to);
+        //reflect the same changes in the document:
+        var me = this.toArray();
+        if(realIndex!=from){
+            var div = me[realIndex]; //me has already been updated
+            div.detach();
+            var parent = this.div;
+            if(to==this.length){
+                parent.append(div);
             }else{
-                this.updateTitleWidth();
+                this.$J( parent.children()[realIndex] ).before(div);
             }
-            
-        },
+        }
 
-        focusOn: function(){
-            this.me.css('backgroundColor','#f5f5c2');
-            this.e_titleText.select();
-        },
+        var t = this;
+        var setIdx = t.setIndex;
+
+        this.each(Math.min(from,realIndex),Math.max(from,realIndex)+1, function(i, div){
+            setIdx.apply(t,[div,i]);
+        });
+
+        this.setOffset(me[realIndex],newOffset);
+
+        //TODO: create a function?
+        this.setEditMode(realIndex,true);
+        this.setFocus(realIndex,true);
+        return realIndex;
+    },
+    //overridden
+    remove : function(index){
+        var div = this._super(index);
+        div.remove();
+        var me = this;
+        this.each(index,function(i, div){
+            me.setIndex.apply(me,[div,i]);
+        });
+    },
+    //overridden
+    makeTimeLabel: function(time){
+        return this._super(time,['hh','mm','ss','C']);
+    },
+    //overridden
+    clear: function(){
+        var divs = this._super();
+        for(var i=0; i< divs.length; i++){
+            divs[i].empty().remove();
+        }
+        return divs;
+    },
+    //if value is missing, toggles edit mode
+    //if editbutton is not present (marker not editable), this method does nothing
+    setEditMode: function(index, value){
+        
+        var div = this.toArray()[index];
+        var editButton = div.find('.markersdivEdit');
+        if(!((editButton) && (editButton.length))){
+            return;
+        }
+        var visible = editButton.is(':visible');
+
+        if(arguments.length==1){ //toggle
+            value = visible; //if edit visible, editmode = true, otherwise false
+        }else if(value!=visible){ 
+            //value is defined. if true and edit mode is NOT visible, we return cause we are already in edit mode
+            //same if false (dont edit) and edit mode is visible (not edit mode)
+            return;
+        }
+        var e_okButton = div.find('.markersdivSave');
+       
+        var e_descriptionText = div.find('.markersdivDescription');
+        var e_titleText = div.find('.markersdivTitle');
+        if(value){
+            this.debug('setting ba bla bla');
+            e_descriptionText.removeAttr('readonly').removeClass('markersdivUneditable');
+            e_titleText.removeAttr('readonly').removeClass('markersdivUneditable');
+            e_okButton.show();
+            e_titleText.select(); //TODO: this does NOT set the focus on the div. Why?
+            editButton.hide();
+            //e_titleText.focus();
+        }else{
+            e_descriptionText.attr('readonly','readonly').addClass('markersdivUneditable');
+            e_titleText.attr('readonly','readonly').addClass('markersdivUneditable');
+            e_okButton.hide();
+            editButton.show();
+        }
 
-        focusOff: function(){
-            this.me.css('backgroundColor','');
-        },
+        this.stretch(e_titleText);
+    },
 
-        updateTitleWidth: function(tText){
-            if(!(tText)){
-                tText = this.e_titleText;
-            }
-            if(tText){
-                var w = tText.parent().width();
-                w-=tText.outerWidth(true)-tText.width(); //so we consider also tText margin border and padding
-                var space = w
-                - (this.e_addplaylistButton.is(':visible') ? this.e_addplaylistButton.outerWidth(true) : 0)
-                - (this.e_indexLabel.is(':visible') ? this.e_indexLabel.outerWidth(true) : 0)
-                - (this.e_offsetLabel.is(':visible') ? this.e_offsetLabel.outerWidth(true) : 0)
-                - (this.e_editButton.is(':visible') ? this.e_editButton.outerWidth(true) : 0)
-                - (this.e_deleteButton.is(':visible') ? this.e_deleteButton.outerWidth(true) : 0);
-                tText.css('width',space+'px');
+    setFocus: function(index,value){
+        this.each(function(i,div){
+            if(i==index && value){
+                div.css('backgroundColor','#f5cf23'); //'#efc823'
+            }else{
+                div.css('backgroundColor','');
             }
-        },
-
-        remove: function(){
-            this.me.remove();
-            this.e_indexLabel = null;
-            this.e_descriptionText=null;
-            this.e_offsetLabel=null;
-            this.e_deleteButton=null;
-            this.e_okButton=null;
-            this.e_header=null;
-            this.e_editButton=null;
-            this.e_titleText=null;
-            this.me=null;
-        },
-
-        formatMarkerOffset: function(markerOffset){
-            //marker offset is in float format second.decimalPart
-            var hours = parseInt(markerOffset/(60*24));
-            markerOffset-=hours*(60*24);
-            var minutes = parseInt(markerOffset/(60));
-            markerOffset-=minutes*(60);
-            var seconds = parseInt(markerOffset);
-            markerOffset-=seconds;
-            var msec = Math.round(markerOffset*100); //show only centiseconds
-            //(use 1000* to show milliseconds)
-            var format = (hours<10 ? "0"+hours : hours )+":"+
-            (minutes<10 ? "0"+minutes : minutes )+":"+
-            (seconds<10 ? "0"+seconds : seconds )+"."+
-            (msec<10 ? "0"+msec : msec );
-            return format;
+        });
+    },
+
+
+    setIndex: function(div,index){
+        //div.attr('id','_markerdiv'+index);
+        div.find('.ts-marker').html(index+1);
+        var me = this;
+        div.find('.markersdivDescription').unbind('focus').focus(function(){
+            me.setFocus(index,true);
+        });
+        div.find('.markersdivTitle').unbind('focus').focus(function(){
+            me.setFocus(index,true);
+        });
+        div.find('.markersdivEdit').unbind('click').click( function(){
+            me.setEditMode(index);
+            return false; //avoid scrolling of the page on anchor click
+        });
+    },
+/**
+ * stretches jQueryElm the whole possible width. Note that text nodes are not considered!!!!
+ */
+    stretch: function(jQueryElm){
+      var siblings = jQueryElm.siblings(":visible");
+      siblings = siblings.add(jQueryElm);
+      var spaceStretchable = jQueryElm.parent().width();
+      var $J = this.$J;
+      siblings.each(function(i,elm){
+          spaceStretchable -= $J(elm).outerWidth(true);
+          //consolelog("\t"+spaceStretchable+' elm:'+$J(elm).attr('class')+" left: "+$J(elm).position().left+" outerw:" +$J(elm).outerWidth(true)+" w: "+$J(elm).width());
+      });
+      //consolelog('w'+ jQueryElm.parent().width()+' elm.w: '+jQueryElm.width()+' spacestretchable: '+spaceStretchable);
+      var w = jQueryElm.width() + spaceStretchable;
+      jQueryElm.css('width', w+'px');
+    },
+
+    setOffset: function(div,offset){
+        div.find('.markersdivOffset').html(this.makeTimeLabel(offset));
+    },
+    createMarkerDiv : function(index, marker){
+        //TODO: why class 'ts-marker' does not work?
+        //for the moment we set the style manually, remove
+        //TODO: table width with CSS?
+        var div = this.$J('<div/>').addClass("markerdiv").html('<div>'+
+                                '<a class="ts-marker"></a>'+
+                                '<span class="markersdivOffset" type="text"></span>'+
+                                '<input class="markersdivTitle" type="text"/>'+
+                                '<a class="markersdivAddPlaylist" title="add to playlist"></a>'+
+                                '<a class="markersdivEdit" title="edit">EDIT</a>'+
+                                '<a class="markersdivDelete" title="delete"></a>'+
+                            '</div>'+
+                          '<div zero_top_padding><textarea class="markersdivDescription"></textarea></div>'+
+                          '<div zero_top_padding><a class="markersdivSave">OK</a></div>'); //TODO: avoid text nodes
+        div.find('a').attr('href','#');
+        //todo: remove markerlabel from css!!!!!!!
+        //new RulerMarker(div.find('.markerlbl'),div.find('.markercanvas'),'marker',false);
+
+        var e_indexLabel = div.find('.ts-marker');
+        //var e_offsetLabel =div.find('.markersdivOffset');
+        var e_okButton = div.find('.markersdivSave');
+        var e_editButton = div.find('.markersdivEdit');
+        var e_deleteButton =  div.find('.markersdivDelete');
+        var e_addplaylistButton = div.find('.markersdivAddPlaylist');
+        var e_descriptionText = div.find('.markersdivDescription');
+        var e_titleText = div.find('.markersdivTitle');
+
+        //set defualt element values regardeless of the marker state
+        e_indexLabel.attr('title',marker.toString());
+        this.setIndex(div, index);
+            
+        //e_offsetLabel.html(this.makeTimeLabel(marker.offset));
+        this.setOffset(div,marker.offset);
+        //set visibility and attach events according to the marker state:
+        //first, is editing or not
+        //var isEditing = marker.isEditable && marker.isModified;
+        //            (!marker.isSavedOnServer || !(this.e_editButton.is(':visible')));
+
+        //if(!isEditing){
+        e_descriptionText.val(marker.desc ? marker.desc : "");
+        e_titleText.val(marker.title ? marker.title : "");
+        //}
+
+        e_okButton.hide();
+        e_editButton.show();
+        e_deleteButton.show();
+        e_addplaylistButton.show();
+        e_descriptionText.attr('readonly','readonly').addClass('markersdivUneditable').unbind('focus');
+        e_titleText.attr('readonly','readonly').addClass('markersdivUneditable').unbind('focus');
+
+
+        if(!marker.isEditable){
+            e_editButton.hide();
+            e_deleteButton.hide();
+            //we unbind events to be sure
+            e_addplaylistButton.unbind('click').hide();
+            e_okButton.unbind('click')
+            e_deleteButton.unbind('click').hide();
+            e_editButton.remove(); //so that if edit button is not present, we do not edit (safety reasons) see this.setEditMode
+            return div;
         }
-   
-
-
-    });
-
-    $N.notifyScriptLoad();
-
-});
-
-
-Object.prototype.toString = function(){
-    var s="";
-    for(var k in this){
-        s+=k+": "+this[k]+"\n";
+        
+        var me = this;
+        e_deleteButton.unbind('click').click( function(){
+            if(!(marker.isSavedOnServer) || confirm('delete the marker permanently?')){
+                me.fire('remove',{
+                    'marker':marker
+                });
+            }
+            return false; //avoid scrolling of the page on anchor click
+        })
+
+        e_addplaylistButton.unbind('click').bind('click',function(evtObj_){
+            playlistUtils.showPopupAddToPlaylist(evtObj_,'marker',""+marker.id,'marker added to selected playlist');
+            return false;
+        });
+
+        //action for ok button
+        e_okButton.unbind('click').click( function(){
+            //if(marker.desc !== descriptionText.val()){ //strict equality needed. See note below
+            marker.desc = e_descriptionText.val();
+            marker.title = e_titleText.val();
+            me.fire('save',{
+                'marker':marker
+            });
+            return false; //avoid scrolling of the page on anchor click
+        });
+
+        //            if(isEditing){
+        //                startEdit();
+        //            }else{
+        //                this.updateTitleWidth();
+        //            }
+        return div;
     }
-    return s;
-}
\ No newline at end of file
+
+});
\ No newline at end of file
index b7ecf7b141c7c78e19ea4016102b67ab36bbd2d2..839a2a25e566866e908f6c29e5ea4728445ff792 100644 (file)
 /**
  * TimeSide - Web Audio Components
- * Copyright (c) 2008-2009 Samalyse
- * Author: Olivier Guilyardi <olivier samalyse com> and Riccardo Zaccarelli
+ * Copyright (c) 2011 Parisson
+ * Author: Riccardo Zaccarelli
  * License: GNU General Public License version 2.0
  */
+var MarkerMap = TimesideArray.extend({
 
-TimeSide(function($N, $J) {
+    init: function() {
+        this._super();
+        var ui = uniqid; //defined in application.js (global vars and functions)
+        this.uniqid = function(){
+            return  ui();
+        };
+    },
 
-    $N.Class.create("MarkerMap", $N.Core, {
-        markers: null,
-        //the main div container:
-        divContainer: $J("#markers_div_id"),
-        initialize: function($super, markers) {
-            $super();
-            if (!markers){
-                markers = [];
-            }
-            this.markers = markers;
-        },
-   
-        get: function(index){
-            return this.markers[index];
-
-        },
+    //overridden
+    add: function(obj) {
+        //var markers = this.toArray();
+        var marker = this.createMarker(obj);
+        var idx = this.insertionIndex(marker);
+        if(idx>=0){ //it exists? there is a problem....
+            this.debug('adding a marker already existing!!'); //should not happen. however...
+            return -1;
+        }
+       
+        idx = -idx-1;
+        //we do not call the super add cause we want to insert at a specified index
+        this._super(marker,idx);
+        //notifies controller.onMarkerMapAdd
 
+        this.fire('add', {
+            marker: marker,
+            index: idx,
+            isNew: (typeof obj == 'number' || typeof obj == 'string')
+        });
+        //var temp = new MarkerDiv();
+        // this.debug(this.createMarkerDiv());
+         
+            
+        return idx;
+    },
+    //TODO: remove from here
+        
 
-        add: function(obj) {
-            var marker = this.createMarker(obj);
-            var idx = this.insertionIndex(marker);
+    //argument is either an object loaded from server or a number specifying the marker offset
+    createMarker: function(argument){
+        var marker = null;
+        var pFloat = parseFloat;
+        if(typeof argument == 'string'){ //to be sure, it might be that we pass an offset in string format
+            argument = pFloat(argument);
+        }
+        if(typeof argument == 'object'){
+            var editable = CURRENT_USER_NAME === argument.author;
+            marker = {
+                id: argument.public_id,
+                offset: pFloat(argument.time), //IMPORTANT: IT IS A STRING!!!!!!
+                desc: argument.description,
+                title: argument.title,
+                author: argument.author,
+                isEditable: editable,
+                isSavedOnServer: true
+            };
+        }else if(typeof argument == 'number'){
+            marker = {
+                id: this.uniqid(),
+                offset: pFloat(argument),
+                desc: "",
+                title: "",
+                author: CURRENT_USER_NAME,
+                isEditable: true,
+                isSavedOnServer: false
+            };
+        }
+        marker.toString = function(){
+            var props = [];
+            for(var prop in this){
+                if(!(prop == 'toString')){
+                    props.push(prop+': '+this[prop]);
+                }
+            }
+            return props.sort().join("\n");
+        }
+        return marker;
 
-            //if exists (ix>0) add it AFTER the existing item
-            idx = idx<0 ? -idx-1 : idx+1;
+    },
 
-            this.markers.splice(idx,0,marker);
-            //notifies controller.onMarkerMapAdd
-            this.fire('add', {
-                marker: marker,
-                index: idx
+    //overridden
+    //markerOrIndex can be an number (marker index) or a marker (the index will be aearched)
+    remove: function(identifier) {
+        var idx = -1;
+        if(typeof index == 'number'){
+            idx = identifier;
+        }else{
+            idx = this.insertionIndex(identifier);
+        }
+        if(idx<0 || idx>=this.length){
+            this.each(function(i,m){
+                consolelog(m);
             });
-            return idx;
-        },
-
-        //argument is either an object loaded from server or a number specifying the marker offset
-        createMarker: function(argument){
-            var marker = null;
-            if(typeof argument == 'string'){ //to be sure, it might be that we pass an offset in string format
-                argument = parseFloat(argument);
-            }
-            if(typeof argument == 'object'){
-                var editable = CURRENT_USER_NAME === argument.author;
-                marker = {
-                    id: argument.public_id,
-                    offset: argument.time,
-                    desc: argument.description,
-                    title: argument.title,
-                    author: argument.author,
-                    isEditable: editable,
-                    isSavedOnServer: true,
-                    isModified:false
-                };
-            }else if(typeof argument == 'number'){
-                marker = {
-                    id: this.uniqid(),
-                    offset: parseFloat(argument),
-                    desc: "",
-                    title: "",
-                    author: CURRENT_USER_NAME,
-                    isEditable: true,
-                    isSavedOnServer: false,
-                    isModified: true
-                };
-            }
-            return marker;
+            consolelog(identifier);
+            //TODO: handle error
+            this.debug('remove: marker not found');
+            return;
+        }
 
-        },
+        //build the function to be called if the marker is deleted
+        //if the marker is NOT saved on server, call the function immediately
+        var marker = this.toArray()[idx];
+        var me = this;
+        var superRemove = me._super;
+        var functionOnSuccess = function(){
+            superRemove.apply(me,[idx]);
+            me.fire('remove',{
+                    'index':idx
+                })
+        }
 
-        remove: function(index) {
-            var marker = this.get(index);
-            if (marker) {
-                if(marker.isSavedOnServer){
-                    this.removeHTTP(marker);
-                }
-                this.markers.splice(index, 1);
-                //notifies controller.js
-                this.fire('remove', {
-                    index: index
-                });
-            }
-            return marker;
-        },
+        if(marker.isSavedOnServer){
+             //json(param,method,onSuccessFcn,onErrorFcn){
+            json([marker.id], "telemeta.del_marker",functionOnSuccess);
+        }else{
+            functionOnSuccess();
+        }
+    },
 
-        move: function(markerIndex, newOffset){
-            var newIndex = this.indexOf(newOffset);
-            var realIndex = this.markers.move(markerIndex,newIndex);
+    save: function(marker){
+        var idx = this.insertionIndex(marker);
+        if(idx<0 || idx>=this.length){
+            //TODO: habdle error
+            this.debug('marker not found');
+        }
+        
+        //TODO: item public id defined elsewhere up, not here inside
+        var itemid = ITEM_PUBLIC_ID;
+        var isSavedOnServer = marker.isSavedOnServer;
+        var method = isSavedOnServer ? "telemeta.update_marker" : "telemeta.add_marker";
+        var param = {
+            'item_id':itemid,
+            'public_id': marker.id,
+            'time':marker.offset,
+            'author': marker.author,
+            'title':marker.title,
+            'description':marker.desc
+        };
 
-            var marker = this.markers[realIndex];
-            marker.offset = newOffset;
-            marker.isModified = true;
-            
-            this.fire('moved', {
-                fromIndex: markerIndex,
-                toIndex: newIndex,
-                newIndex: realIndex
+        //function on success:
+        var me = this;
+        var success = function(){
+            if(!isSavedOnServer){
+                marker.isSavedOnServer = true;
+                marker.isModified = false;
+            }
+             me.fire('save',{
+                'index':idx
             });
+        };
+        //json(param,method,onSuccessFcn,onErrorFcn){
+        json([param], method, success);
+
+    },
 
+    //TODO: there is no need of a public method
+//    removeHTTP: function(marker){
+//        var public_id = marker.id
+//        //json(param,method,onSuccessFcn,onErrorFcn){
+//        json([public_id], "telemeta.del_marker");
+//    },
 
-        },
-        //
-        //The core search index function: returns insertionIndex if object is found according to comparatorFunction,
-        //(-insertionIndex-1) if object is not found. This assures that if the returned
-        //number is >=0, the array contains the element, otherwise not and the element can be inserted at
-        //-insertionIndex-1
-        insertionIndex: function(object){
+    //overridden method
+    move: function(markerIndex, newOffset){
+        var newIndex = this.insertionIndex(newOffset);
+        //select the case:
+        if(newIndex<0){
+            //we didn't move the marker on another marker (newOffset does not correspond to any marker)
+            //just return the real insertionIndex
+            newIndex = -newIndex-1;
+        }
+        //            var markers = this.getMarkers();
+        //            //TODO: remove move from array prototype!!!!
+        var realIndex = this._super(markerIndex,newIndex);
+        //            //var realIndex = markers.move(markerIndex,newIndex);
+        //            this.debug('fromindex '+markerIndex+' to: '+newIndex+' results in '+realIndex);
+        var markers = this.toArray();
+        var marker = markers[realIndex];
+        marker.offset = newOffset;
+        marker.isModified = true;
+        this.fire('move', {
+            fromIndex: markerIndex,
+            toIndex: newIndex,
+            newOffset: newOffset
+        //,newIndex: realIndex
+        });
+    },
+       
+    
+    //returns the insertion index of object in this sorted array by means of a binary search algorithm.
+    // A) If object is a marker and:
+    //   a1) Is found (ie, there is a marker in this map
+    //       with same offset and same id), returns the index of the marker found, in the range [0, this.length-1]. Otherwise, if
+    //   a2) Is not found, then returns -(insertionIndex-1), where insertionIndex is the
+    //       index at which object would be inserted preserving the array order. Note that this assures that a
+    //       number lower than zero means that object is not present in the array, and viceversa
+    // B) If object is a number or a string number (eg, "12.567"), then a marker with offset = object is built and compared 
+    //    against the markers in the map. Note however that in this case that equality between marker's offset is sufficient,
+    //    as object is not provided with an id. THEREFORE, IF THE MAP CONTAINS SEVERAL MARKERS AT INDICES i, i+1, ... i+n
+    //    WITH SAME OFFSET == object, THERE IS NO WAY TO DETERMINE WHICH INDEX IN [i, i+1, ... i+n] WILL BE RETURNED.
+    //    See player.forward and player.rewind for an example of the B) case.
+    //LAST NOTE: BE SURE object is either a number (float) or object.offset is a number (float).
+    //In case it is not known, If it is a string number such as
+    //"4.562" the comparison falis (eg, "2.567" > "10.544") but obviously, no error is thrown in javascript
+    //
+    insertionIndex: function(object){
+        //default comparator function:
+        //returns 1 as the first argument is greater than the second
+        //returns -1 as the first argument is lower than the second
+        //returns 0 if the arguments are equal
+        var comparatorFunction = function(markerInMap,newMarker){
+            var a = markerInMap.offset;
+            var b = newMarker.offset;
+            if(a<b){
+                return -1;
+            }else if(a >b){
+                return 1;
+            }else{
+                var a1 = markerInMap.id;
+                var b1 = newMarker.id;
+                if(a1<b1){
+                    return -1;
+                }else if(a1>b1){
+                    return 1;
+                }
+            }
+            return 0;
+            //var ret = a < b ? -1 : (a>b ? 1 : (markerInMap.id === newMarker.id ? 0 : -1));
+            //return ret;
+        };
+        if(!(typeof object == 'object')){
             var offset;
-            if(typeof object == 'object'){
-                offset = object.offset;
-            }else if(typeof object == 'number'){
+            if(typeof object == 'number'){
                 offset = object;
             }else{ //to be sure...
                 offset = parseFloat(object);
             }
-            var pInt = parseInt; //reference to parseInt (to increase algorithm performances)
-            var comparatorFunction = function(a,b){
-                return (a<b ? -1 : (a>b ? 1 : 0));
+            object = {
+                'offset':offset
             };
-            var data = this.markers;
-            var low = 0;
-            var high = data.length-1;
-
-            while (low <= high) {
-                //int mid = (low + high) >>> 1;
-                var mid = pInt((low + high)/2);
-                var midVal = data[mid];
-                var cmp = comparatorFunction(midVal.offset,offset);
-                if (cmp < 0){
-                    low = mid + 1;
-                }else if (cmp > 0){
-                    high = mid - 1;
-                }else{
-                    return mid; // key found
-                }
-            }
-            return -(low + 1);  // key not found
-        },
-        //indexOf is the same as insertionIndex, but returns a positive number.
-        //in other words, it is useful when we do not want to know if obj is already present
-        //in the map, but only WHERE WOULD be inserted obj in the map. obj can be a marker
-        //or an offset (time). In the latter case a dummy marker with that offset will be considered
-        indexOf: function(obj){
-            var idx = this.insertionIndex(obj);
-            return idx<0 ? -idx-1 : idx;
-        },
-        each: function(callback) {
-            $J(this.markers).each(callback);
-        },
-        //        length: function(){
-        //            return this.markers ? this.markers.length : 0;
-        //        },
-       
-
-        sendHTTP: function(marker, functionOnSuccess, showAlertOnError){
-            var itemid = ITEM_PUBLIC_ID;
-             var isSaved = marker.isSavedOnServer;
-            var method = isSaved ? "telemeta.update_marker" : "telemeta.add_marker";
-            var param = {
-                'item_id':itemid,
-                'public_id': marker.id,
-                'time':marker.offset,
-                'author': marker.author,
-                'title':marker.title,
-                'description':marker.desc
+            //key will never be found, so return either 1 or -1:
+            comparatorFunction = function(markerInMap,newMarker){
+                var a = markerInMap.offset;
+                var b = newMarker.offset;
+                return a < b ? -1 : (a>b ? 1 : 0);
             };
-            var success = function(){
-                    if(!isSaved){
-                        marker.isSavedOnServer = true;
-                        marker.isModified = false;
-                    }
-                    if(functionOnSuccess){
-                        functionOnSuccess();
-                    }
-                };
-            //json(param,method,onSuccessFcn,onErrorFcn){
-            json([param], method, success);
-            
-        },
-
-        removeHTTP: function(marker){
-             var public_id = marker.id
-            //json(param,method,onSuccessFcn,onErrorFcn){
-            json([public_id], "telemeta.del_marker");
         }
+        var pInt = parseInt; //reference to parseInt outside the loop below
+        //(to increase algorithm performances)
 
-    });
-
-    $N.notifyScriptLoad();
+        var data = this.toArray();
+        var low = 0;
+        var high = data.length-1;
 
-});
+        while (low <= high) {
+            //int mid = (low + high) >>> 1;
+            var mid = pInt((low + high)/2);
+            var midVal = data[mid];
+            var cmp = comparatorFunction(midVal,object);
+            if (cmp < 0){
+                //the midvalue is lower than the searched index element
+                low = mid + 1;
+            }else if (cmp > 0){
+                //the midvalue is greater than the searched index element
+                high = mid - 1;
+            }else{
+                return mid; // key found
+            }
+        }
+        return -(low + 1);  // key not found
+    }
+}
+);
\ No newline at end of file
index f9719c5c70437710e10f0f82d45a3fd2375a4879..f522475973e28d7e2f041e611fd9ffa72e82abcb 100644 (file)
-/**
- * TimeSide - Web Audio Components
- * Copyright (c) 2008-2009 Samalyse
- * Author: Olivier Guilyardi <olivier samalyse com>
- * License: GNU General Public License version 2.0
- */
-
-TimeSide(function($N, $J) {
-
-    $N.Class.create("Player", $N.Core, {
-        skeleton: {
-            'div.viewer': {
-                'div.ruler': {},
-                'div.wave': {
-                    'div.image-canvas': {},
-                    'div.image-container': ['img.image']
-                }
-            },
-            'div.control': {
-                'div.layout': {
-                    'div.playback': ['a.play', 'a.pause', 'a.rewind', 'a.forward', 'a.set-marker' //]
-                    ,'a.volume']
-                }
-            }/*,
-        'div.marker-control': ['a.set-marker']*/
-        },
-        defaultContents: {
-            play: 'Play',
-            pause: 'Pause',
-            rewind: 'Rewind',
-            forward: 'Forward',
-            'set-marker': 'Set marker'
-        //,'text-marker' : 'textmarker'
-        },
-        elements: {},
-        ruler: null,
-        map: null,
-        container: null,
-        imageWidth: null,
-        imageHeight: null,
-
-        initialize: function($super, container, cfg) {
-            $super();
-            if (!container){
-                throw new $N.RequiredArgumentError(this, 'container');
-            }
-            this.container = $J(container);
-            
-            this.configure(cfg, {
-                image: null,
-                sound:null,
-                soundDurationInMsec:0
-            });
-        //if(this.cfg.sound && this.cfg.sound.)
-        },
-
-        free: function($super) {
-            this.elements = null;
-            this.container = null;
-            //this.sound.destruct(); //should be called here?
-            $super();
-        },
-
-
-        setMarkerMap: function(map) {
-            this.map = map;
-            return this;
-        },
-
-        setImage: function(expr) {
-            this.cfg.image = expr;
-            this.refreshImage();
-        },
-
-        refreshImage: function() {
-            var src = null;
-            if (typeof this.cfg.image == 'function') {
-                src = this.cfg.image(this.imageWidth, this.imageHeight);
-            } else if (typeof this.cfg.image == 'string') {
-                src = this.cfg.image;
-            }
+var Player = TimesideClass.extend({
+
+    //sound duration is in milliseconds because the soundmanager has that unit,
+    //player (according to timeside syntax) has durations in seconds
+    init: function(container, sound, soundDurationInMsec) {
+        this._super();
+        if (!container){
+            this.debug('ERROR: container is null in initializing the player')
+        }
+        this.getContainer = function(){
+            return container;
+        }
+        this.getSound = function(){
+            return sound;
+        }
 
-            if (src) {
-                this.elements.image.attr('src', src);
-            }
-        },
-
-        //        draw: function() {
-        //            this.debug('drawing');
-        //            $N.domReady(this.attach(this._setupInterface));
-        //            return this;
-        //        },
-
-        _setupInterface: function() {
-            consolelog('player _setupInterface sound.readyState:'+this.cfg.sound.readyState); //handle also cases 0 and 2????
-            //0 = uninitialised
-            //1 = loading
-            //2 = failed/error
-            //3 = loaded/success
-            if(this.cfg.sound.readyState==1){
-                var rsz = this.resize;
-                //attach an event when fully loaded and repaint all.
-                //For the moment we will display the ruler and other stuff
-                //based on durationEstimate property
-                this.cfg.sound.options.onload = function() {
-                    rsz();
-                }
-            }
-            this.elements = $N.Util.loadUI(this.container, this.skeleton, this.defaultContents);
+        //rpivate functions for converting
+        //soundmanager has milliseconds, we use here seconds
+        var pInt = Math.round; //instantiate once for faster lookup
+        var pFloat = parseFloat; //instantiate once for faster lookup
+        function toMsec(seconds){
+            return pInt(seconds*1000);
+        }
+        function toSec(msec){
+            return pFloat(msec)/1000;
+        }
 
-            // IE apparently doesn't send the second mousedown on double click:
-            var jump = $J.browser.msie ? 'mousedown dblclick' : 'mousedown';
-            this.elements.rewind.attr('href', '#').bind(jump, this.attach(this._onRewind))
-            .click(function() {
-                return false;
-            });
-            this.elements.forward.attr('href', '#').bind(jump, this.attach(this._onForward))
-            .click(function() {
+
+
+        var sd = toSec(soundDurationInMsec);
+        this.getSoundDuration = function(){
+            return sd;
+        }
+       
+        this.isPlaying = function(){
+            /*Numeric value indicating the current playing state of the sound.
+             * 0 = stopped/uninitialised
+             * 1 = playing or buffering sound (play has been called, waiting for data etc.)
+             *Note that a 1 may not always guarantee that sound is being heard, given buffering and autoPlay status.*/
+            return sound && sound.playState==1;
+        };
+        
+        //setting the position===============================================
+        //if sound is not loaded, position is buggy. Moreover, we have to handle the conversions between units: 
+        //seconds (here) and milliseconds (swmanager sound). So we store a private variable
+        //private variable and function
+        var soundPos = sound.position ? toSec(sound.position) : 0.0;
+        //private method: updates just the internal variable (called in whilePlaying below)
+        function setPos(value){
+            soundPos = value;
+        }
+        //public methods: calls setPos above AND updates sounbd position
+        this.setSoundPosition = function(newPositionInSeconds){
+            //for some odd reason, if we set sound.setPosition here soundPos
+            //is rounded till the 3rd decimal integer AND WILL BE ROUNDED THIS WAY IN THE FUTURE
+            //don't know why, however we set the sound position before playing (see below)
+            //however, now it works. Even odder....
+            setPos(newPositionInSeconds);
+            if(sound){
+                var s = toMsec(this.getSoundPosition());
+                sound.setPosition(s);
+            }
+        }
+        //public methods: returns the sound position
+        this.getSoundPosition = function(){
+            return soundPos;
+        };
+
+        
+        //implement play here: while playing we do not have to update the sound position, so
+        //we call the private variable soundPos
+        this.play = function(){
+            var player = this;
+            if(!player || player.isPlaying()){
                 return false;
-            });
-            
-            //
-            this.elements.volume.attr('href', '#').click(function(){
+            }
+            var sound = player.getSound();
+            if(!sound){
                 return false;
-            }).bind('mousedown', this.attach(
-                function(e){
-                    if(e.which===1){ //left button
-                        this.setVolume(e);
-                    }
-                    return false;
-                }
-                ));
+            }
 
+            var map = player.getMarkerMap();
+            var indexToShow = map.insertionIndex(player.getSoundPosition());
+            
+            var mydiv = this.$J('<div/>').addClass('markerDiv').css({'position':'absolute','zIndex':1000});
 
-            //assigning title string to all anchors???????
-            this.elements.control.find('a').add(this.elements.setMarker)
-            .attr('href', '#')
-            .each(function(i, a){
-                a = $J(a);
-                if (!a.attr('title')){
-                    a.attr('title', a.text());
-                }
-            });
+            var ruler = player.getRuler();
             
-            //this.elements.markerControl.find('a').attr('href', '#');
-            if (this.map && CURRENT_USER_NAME) {
-                //configureMarkersDiv();
-                this.elements.setMarker.bind('click', this.attach(this._onSetMarker));
-            //this.elements.setMarker2.bind('click', this.attach(this._onSetMarker2));
-            //this.elements.textMarker.attr('type', 'text');
-            //this.elements.textMarker.bind('click', this.attach(this._onSetMarker2));
-          
-            } else {
-                this.elements.setMarker.remove();
-            }
-            //creating the ruler
-            var ruler = new $N.Ruler({
-                viewer: this.elements.viewer,
-                //map: this.map,
-                sound: this.cfg.sound,
-                soundDurationInMsec: this.cfg.soundDurationInMsec
-            });
-            this.ruler = ruler;
-            //bind events to the ruler (see function observe in core.js, I guess,
-            //which overrides jQuery bind function):
-            //the first arg is basically the event name, the second
-            //arg is a function to execute each time the event is triggered
-            this.ruler
-            .observe('markermove', this.forwardEvent)
-            .observe('markeradd', this.forwardEvent)
-            //.observe('move', this.forwardEvent)
-            .draw();
-            this.refreshImage();
-            this.resize(); 
-
-            //            var resizeTimer = null;
-            //            $J(window).resize(this.attach(function() {
-            //                if (resizeTimer){
-            //                    clearTimeout(resizeTimer);
-            //                }
-            //                resizeTimer = setTimeout(this.attach(this.resize), 100);
-            //            }));
-
-            this.setSoundVolume(this.getSoundVolume());
-            //finally, binds events to play and pause. At the end cause this.ruler has to be fully initialized
-            var sound = this.cfg.sound;
-            this.elements.pause.attr('href', '#').bind('click', function(){
-                sound.pause();
-                return false;
-            });
-            //var r = this.ruler;
-            var player = this;
             var playOptions = {
                 whileplaying: function(){
-                    ruler._movePointer(this.position/1000); //this will refer to the sound object
-                }
-            };
-            this.elements.play.attr('href', '#').bind('click', function(){
-
-                consolelog('playstate'+sound.playState);
-                consolelog('readystate'+sound.readyState);
-                if(sound.playState!=1 || sound.paused){
-                    //if sound has to be loaded and position is not zero, load it first
-                    if(sound.readyState==0 && player.getSoundPosition()){
-                        sound.options.onload=function(){
-                            this.setPosition(player.getSoundPosition()*1000);
-                            //consolelog('loaded and played from '+player.getSoundPosition() +' '+this.position);
-                            sound.play(playOptions);
+                    var sPos = toSec(this.position); //this will refer to the sound object (see below)
+                    setPos(sPos);
+                    if(ruler && !ruler.isPointerMovingFromMouse()){
+                        ruler.movePointer(sPos);
+                    }
+                    if(indexToShow<map.length){
+                        //consolelog(map.toArray()[indexToShow].offset+' '+sPos);
+                        var spanSec = 0.25;
+                        var offzet = map.toArray()[indexToShow].offset;
+                        if(offzet>=sPos-spanSec && offzet<=sPos+spanSec){
+                            indexToShow++;
+                            popup.show(jQuery('<div/>').html(map.toArray()[indexToShow-1].toString()));
+                            consolelog('showing marker '+(indexToShow-1));
                         }
-                        sound.load();
-                    }else{
-                        //consolelog('NOT loaded and played');
-                        sound.play(playOptions);
                     }
+                },
+                onfinish: function() {
+                    setPos(0); //reset position, not cursor, so that clicking play restarts from zero
                 }
-                return false;
-            });
-        },
-
-        resize: function(overrideHeight) {
-            this.debug("resizing");
-            var height;
-            if (overrideHeight === true) {
-                this.debug("override height");
-                height = this.elements.image.css('height', 'auto').height();
-            } else {
-                height = this.elements.wave.height();
-                this.debug("wave height:" + height);
-                if (!height) {
-                    this.elements.image.one('load', this.attach(function() {
-                        this.resize(true);
-                        this.debug("image loaded");
-                    }));
-                    height = this.elements.image.height();
+            };
+            //internal play function. Set all properties and play:
+            var play_ = function(sound, positionInSec){
+                sound.setPosition(toMsec(positionInSec));
+                indexToShow = map.insertionIndex(positionInSec); //if we are at zero
+                if(indexToShow<0){
+                    indexToShow = -indexToShow-1;
+                }
+                sound.setVolume(sound.volume); //workaround. Just to be sure. Sometimes it fails when we re-play
+                sound.play(playOptions);
+            };
+            //var s = toMsec(player.getSoundPosition());
+            if(sound.readyState != 3){
+                /*sound.readyState
+                 * Numeric value indicating a sound's current load status
+                 * 0 = uninitialised
+                 * 1 = loading
+                 * 2 = failed/error
+                 * 3 = loaded/success
+                 */
+                sound.options.onload=function(){
+                    //consolelog('LOADED AND PLAY '+' volume '+ sound.volume+' sPos: '+sound.position);
+                    //we set position and volume to be sure. Sometimes they are buggy
+                    //sound.setPosition(s);
+                    //sound.setVolume(sound.volume); //workaround. Just to be sure
+                    //sound.play(playOptions);
+                    play_(sound, player.getSoundPosition());
                 }
+                sound.load();
+            }else{
+                //consolelog('PLAY IMMEDIATELY'+' volume '+ sound.volume+' sPos: '+sound.position);
+                //we set position and volume to be sure. Sometimes they are buggy
+                //                sound.setPosition(s);
+                //                sound.setVolume(sound.volume); //workaround. Just to be sure/
+                //                sound.play(playOptions);
+                play_(sound, player.getSoundPosition());
             }
+            
+            return false;
+        };
+        //now implement also pause here: note that pause has some odd behaviour.
+        //Try this sequence: play stop moveforward moveback play pause
+        //When we press the last pause the sound restarts (??!!!!)
+        this.pause = function(){
+            var sound = this.getSound();
+            //we don't check if it's playing, as the stop must really stop anyway
+            //if(sound && this.isPlaying()){
+            sound.stop();
+            //}
+            return false;
+        };
 
-            var elements = this.elements.image
-            .add(this.elements.imageContainer)
-            .add(this.elements.imageCanvas);
+        //initializing markermap and markerui
+        var map = new MarkerMap();
+        this.getMarkerMap = function(){
+            return map;
+        }
+        var mapUI = new MarkerMapDiv();
+        this.getMarkersUI = function(){
+            return mapUI;
+        }
+    //TODO: define setUpInterface here????
 
-            elements.css('width', 'auto'); // for IE6
+    },
 
-            if (!height){
-                height = 200;
-            }
-            var style = {
-                width: this.elements.wave.width(),
-                height: height
-            }
-            elements.css(style);
-            this.imageWidth = style.width;
-            this.imageHeight = style.height;
-            this.refreshImage();
-            this.ruler.resize();
-            return this;
-        },
-        //sound object methods
-
-        getSoundPosition :function(){
-            //note that this.cfg.sound.position is buggy. If we did not play, calling this.cfg.sound.setPosition(p)
-            //stores the position, but this.cfg.position returns zero.
-            //otherwise (we did play at least once) this.cfg.sound.position returns the good value
-            //to overcome this problem, we return the ruler position, NOTE that it is in seconds
-            return this.ruler.pointerPos;
-        //            var s = this.cfg.sound;
-        //            return s ? s.position/1000 : 0;
-        },
-
-        getSoundVolume :function(){
-            var s = this.cfg.sound;
-            return s ? s.volume : 0;
-        },
-
-        getSoundDuration :function(){
-            var s = this.cfg.sound;
-            return s ? s.duration/1000 : 0;
-        },
-
-        _onRewind: function() {
-            var offset = 0;
-            if (this.map) {
-                var position = parseFloat(this.getSoundPosition());
-                var idx = this.map.indexOf(position)-1;
-                if(idx>=0){
-                    var marker = this.map.get(idx);
-                    if(marker){
-                        offset = marker.offset;
-                    }
-                }
-            }
-            this.ruler._movePointerAndUpdateSoundPosition(offset);
-            return false;
-        },
-
-        _onForward: function() {
-            var offset = this.getSoundDuration();
-            if (this.map) {
-                var position = parseFloat(this.getSoundPosition());
-                var idx = this.map.insertionIndex(position);
-                if(idx>=0){ //the pointer is exactly on a marker, the index is the marker itself
-                    //so increase by one otherwise  and we wouldn't move ahead
-                    //more specifically, increase as long as we have markers with this offset (there could be more than
-                    //one marker at offset
-                    var m = this.map.get(idx);
-                    while(m && m.offset == position){
-                        idx++;
-                        m = this.map.get(idx);
-                        if(!m){
-                            idx=-1;
-                        }
-                    }
-                }else{
-                    //we are not on a pointer, get the index of the marker
-                    //(see markermap insertionindex)
-                    idx = -idx-1;
+    _setupInterface: function(isInteractive) {
+        
+        this.isInteractive = function(){
+            return isInteractive;
+        }
+
+        var sound = this.getSound();
+        consolelog('player _setupInterface sound.readyState:'+sound.readyState); //handle also cases 0 and 2????
+        
+        var $J = this.$J; //defined in the super constructor
+           
+        //TODO: use cssPrefix or delete cssPrefix!!!!!
+        //TODO: note that ts-viewer is already in the html page. Better avoid this (horrible) method and use the html
+        var skeleton =  {
+            'div.ts-viewer': {
+                'div.ts-ruler': {},
+                'div.ts-wave': {
+                    'div.ts-image-canvas': {},
+                    'div.ts-image-container': ['img.ts-image']
                 }
-                if(idx>=0){
-                    var marker = this.map.get(idx);
-                    if(marker){
-                        offset = marker.offset;
-                    }
+            },
+            'div.ts-control': {
+                'div.ts-layout': {
+                    'div.ts-playback': ['a.ts-play', 'a.ts-pause', 'a.ts-rewind', 'a.ts-forward', 'a.ts-set-marker' //]
+                    ,'a.ts-volume']
                 }
-            }
-            this.ruler._movePointerAndUpdateSoundPosition(offset);
+            }/*,
+        'div.marker-control': ['a.set-marker']*/
+        };
+        var jQueryObjs = this.loadUI(this.getContainer(), skeleton);
+
             
-            return false;
-        },
 
-        //notified from a click event on the anchor
-        setVolume: function(event){
+        this.getElements = function(){
+            return jQueryObjs;
+        }
+
+
+
+        var rewind = jQueryObjs.find('.ts-rewind');
+        var forward = jQueryObjs.find('.ts-forward');
+        var play = jQueryObjs.find('.ts-play');
+        var pause = jQueryObjs.find('.ts-pause');
+        var volume = jQueryObjs.find('.ts-volume');
+
+
+        //setting events to buttons (code left untouched from olivier):
+        //rewind
+        //
+        //(olivier comment) IE apparently doesn't send the second mousedown on double click:
+        //        var jump = $J.browser.msie ? 'mousedown dblclick' : 'mousedown';
+        //        rewind.attr('href', '#').bind(jump, this.attach(this._onRewind))
+        //        .click(function() {
+        //            return false;
+        //        });
+        //        //forward:
+        //        forward.attr('href', '#').bind(jump, this.attach(this._onForward))
+        //        .click(function() {
+        //            return false;
+        //        });
+        var me=this;
+        //attaching event to the image. Note that attaching an event to a transparent div is buggy in IE
+//        if($J.browser.msie){
+//
+//        }
+        consolelog('ope');
+        consolelog(jQueryObjs.find('.ts-image').length);
+        jQueryObjs.find('.ts-image').click(function(event){
+            alert('g');
+            consolelog(event);
             
+//            me.setSoundPosition( me.getSoundDuration()/$J(this).width());
+        });
+
+
+        var rewind_ = this.rewind;
+        var forward_ = this.forward;
+        rewind.attr('href', '#').click(function(e){
+            rewind_.apply(me);
+            return false;
+        });
+        forward.attr('href', '#').click(function(e){
+            forward_.apply(me);
+            return false;
+        });
+
+        //volume:
+        function setVolume(event){
             var ticks = [18,26,33,40,47];
             var vol = event.layerX;
             for(var i=0; i<ticks.length; i++){
                 if(vol<=ticks[i]){
-                    //var index = i;
                     var volume = i*20;
-                    this.setSoundVolume(volume);
-                    this.debug('setting volume'+volume);
+                    me.setSoundVolume(volume);
+                    me.debug('setting volume'+volume);
                     return false;
                 }
             }
-            this.setSoundVolume(100);
-            //            var g = 9;
-            //            console.log(event.layerX);
+            me.setSoundVolume(100);
             return false;
-        },
+        }
+        volume.attr('href', '#').click(function(event){
+            return setVolume(event);
+        });
+        //        volume.attr('href', '#').click(function(){
+        //            return false;
+        //        }).bind('mousedown', this.attach(
+        //            function(e){
+        //                if(e.which===1){ //left button
+        //                    this.setVolume(e);
+        //                }
+        //                return false;
+        //            }
+        //            ));
+
+        //assigning title to all anchors
+        jQueryObjs.attr('href', '#')
+        .each(function(i, a){
+            a = $J(a);
+            a.attr('title', a.attr('class').substring(3));
+        });
+        
+        //creating the ruler
+
+        //TODO: why the line below does not work?!!!!!
+        //var viewer = jQueryObjs.find('.ts-viewer');
+        var viewer = this.getContainer().find('.ts-viewer');
+        var ruler = new Ruler(viewer, this.getSoundDuration(), isInteractive);
+        this.getRuler = function(){
+            return ruler;
+        }
+        
+        this.resize(); //which calls also ruler.resize() (see below)
 
-        //TODO: remove unused
-        //        onVolumeChanged: function(e, data){
-        //           this.updateVolumeAnchor(data.volume);
-        //        },
+        //TODO: here? maybe in the constructor
+        this.setSoundVolume(this.getSoundVolume());
 
-        setSoundVolume: function(volume){
-            
-            if(typeof volume != 'number'){ //note: typeof for primitive values, instanceof for the rest
-                //see topic http://stackoverflow.com/questions/472418/why-is-4-not-an-instance-of-number
-                volume = 100;
+
+        //bind events to play and pause.
+        //pause:
+        var pause_ = me.pause;
+        pause.attr('href', '#').bind('click', function(){
+            pause_.apply(me);
+            return false;
+        });
+        //play:
+        var play_ = me.play;
+        play.attr('href', '#').bind('click', function(){
+            play_.apply(me);
+            return false;
+        });
+
+        //finally, load markers and bind events for markers (see method below):
+        this.loadMarkers(isInteractive);
+        
+    },
+    resize: function() {
+        this.debug("resizing");
+        var height;
+        var playerelements = this.getElements();
+        var wave = playerelements.find('.ts-wave');
+        var image = playerelements.find('.ts-image');
+        height = wave.height();
+        this.debug("wave height:" + height);
+        if (!height) {
+            this.debug('ERROR: image height is zero in player.,resize!!!!')
+            height = image.height();
+        }
+        //set image, imagecontainer and canvas (container on imagecontainer for lines and pointer triangles) css
+        var elements = image
+        .add(playerelements.find('.ts-image-container'))
+        .add(playerelements.find('.ts-image-canvas'));
+
+        elements.css('width', 'auto'); // for IE6
+
+        if (!height){
+            height = 200;
+        }
+        var style = {
+            width: wave.width(),
+            height: height
+        }
+        elements.css(style);
+        //this.imageWidth = style.width;
+        //this.imageHeight = style.height;
+        //refreshing images
+        //        var funcImg = function(player_image_url, width, height){
+        //            var _src_ = null;
+        //            if (player_image_url && (width || height)) {
+        //                _src_ = player_image_url.replace('WIDTH', width + '').replace('HEIGHT', height + '');
+        //            }
+        //            return _src_;
+        //        };
+        //        var imgSrc = funcImg(this.getImageUrl(), style.width,style.height);
+        //        if(image.attr('src')!=imgSrc){
+        //            image.attr('src', imgSrc);
+        //        }
+        this.refreshImage(image);
+        this.getRuler().resize();
+        return this;
+    },
+
+    getImageUrl: function(){
+        return this.$J('#visualizer_id').get(0).value;
+    },
+    refreshImage: function(optionalImgTagElm){
+        var image;
+        if(optionalImgTagElm){
+            image = optionalImgTagElm;
+        }else{
+            image = this.getElements().find('.ts-image');
+        }
+        var funcImg = function(player_image_url, width, height){
+            var _src_ = null;
+            if (player_image_url && (width || height)) {
+                _src_ = player_image_url.replace('WIDTH', width + '').replace('HEIGHT', height + '');
+            }
+            return _src_;
+        };
+        var imgSrc = funcImg(this.getImageUrl(), image.width(),image.height());
+        if(image.attr('src')!=imgSrc){
+            consolelog('setting attrt');
+            image.attr('src', imgSrc);
+        }
+    },
+
+    getSoundVolume :function(){
+        var s = this.getSound();
+        return s ? s.volume : 0;
+    },
+    //moves the pointer (and sound position) forward till the next marker or the end of sound
+    forward: function() {
+        var map = this.getMarkerMap();
+        var markers = map.toArray();
+        var len = markers.length;
+        var offset =  this.getSoundDuration();
+        var position = this.getSoundPosition(); //parseFloat(this.getSoundPosition());
+        var idx = map.insertionIndex(position);
+        consolelog('current pointer position: '+position+' '+(typeof position));
+        if(idx<0){
+            idx = -idx-1; //cursor is not on a a marker, get the insertion index
+        }else{
+            //cursor is on a marker. As there might be several markers with the same offset
+            //(see MarkerMap.insertionIndex), move to the outmost right
+            while(idx<len  && markers[idx].offset == position){
+                idx++;
+            }
+        }
+        
+        if(idx< len){
+            offset = markers[idx].offset;
+        }
+        this.setSoundPosition(offset);
+        this.getRuler().movePointer(offset);
+        return false;
+    },
+    //moves the pointer (and sound position) backward till the previous marker or the start of sound
+    rewind: function() {
+        var map = this.getMarkerMap();
+        var markers = map.toArray();
+        var offset =  0;
+        var position = this.getSoundPosition(); //parseFloat(this.getSoundPosition());
+        var idx = map.insertionIndex(position);
+        if(idx<0){
+            idx = -idx-1; //cursor is not on a a marker, get the insertion index
+        }else{
+            //cursor is on a marker. As there might be several markers with the same offset
+            //(see MarkerMap.insertionIndex), move to the outmost left
+            while(idx>0  && markers[idx-1].offset == position){
+                idx--;
             }
-            if(volume<0){
-                volume = 0;
+        }
+        idx--; //move backward (rewind)
+        if(idx>=0){
+            offset = markers[idx].offset;
+        }
+        this.setSoundPosition(offset);
+        this.getRuler().movePointer(offset)
+        return false;
+    },
+
+    setSoundVolume: function(volume){
+
+        if(typeof volume != 'number'){ //note: typeof for primitive values, instanceof for the rest
+            //see topic http://stackoverflow.com/questions/472418/why-is-4-not-an-instance-of-number
+            volume = 100;
+        }
+        if(volume<0){
+            volume = 0;
+        }else if(volume>100){
+            volume = 100;
+        }
+        var sound = this.getSound();
+        //        if(sound.volume == volume){
+        //            return;
+        //        }
+        sound.setVolume(volume);
+        //update the anchor image:
+        var indices = [20,40,60,80,100,100000];
+
+        var volumeElm = this.getElements().find('.ts-volume');
+        for(var i=0; i <indices.length; i++){
+            if(volume<indices[i]){
+                var pos = -28*i;
+                pos = '0px '+ pos+ 'px'; //DO NOT SET !important as in FF3 DOES NOT WORK!!!!!
+                volumeElm.css('backgroundPosition',pos);
+                return;
             }
-            if(volume>100){
-                volume = 100;
+        }
+    // this.elements.volume.css('backgroundPosition','0px 0px !important')
+
+    },
+        
+    loadMarkers: function(isInteractive_){
+        //ruler.bind('markermoved',this.markerMoved,this);
+
+        var itemId = ITEM_PUBLIC_ID;
+
+        var player = this;
+        //initialize the map.
+        var map = this.getMarkerMap();
+        var mapUI = this.getMarkersUI();
+        var ruler = this.getRuler();
+        map.clear();
+        mapUI.clear();
+        ruler.clear();
+        
+        //building the onSuccess function
+        var onSuccess = function(data) {
+            var tabIndex = 0;
+            var mapuiAdd = mapUI.add;
+            var rulerAdd = ruler.add;
+
+            if(data && data.result && data.result.length>0){
+                var result = data.result;
+                //add markers to the map. No listeners associated to it (for the moment)
+                var mapAdd = map.add;
+                for(var i =0; i< result.length; i++){
+                    mapAdd.apply(map,[result[i]]);
+                }
+                //add markers to ruler and div
+                map.each(function(i,marker){
+                    rulerAdd.apply(ruler,[marker, i]);
+                    mapuiAdd.apply(mapUI,[marker, i]);
+                });
+
+                tabIndex = result.length>0 ? 1 : 0;
             }
-            this.cfg.sound.setVolume(volume);
-            //update the anchor image:
-            var indices = [20,40,60,80,100,100000];
-            
-            for(var i=0; i <indices.length; i++){
-                if(volume<indices[i]){
-                    var pos = -28*i;
-                    pos = '0px '+ pos+ 'px'; //DO NOT SET !important as in FF3 DOES NOT WORK!!!!!
-                    this.elements.volume.css('backgroundPosition',pos);
-                    return;
+            //BINDINGS:
+            //
+            //1) ADD
+            //
+            //add binding to the setMarker button (html anchor):
+            var setMarkerButton = player.getElements().find('.ts-set-marker');
+            if(setMarkerButton){
+                if(isInteractive_){
+                    setMarkerButton.show().attr('href','#').unbind('click').bind('click', function(){
+                        map.add(player.getSoundPosition());
+                        return false;
+                    });
+                }else{
+                    setMarkerButton.hide().unbind('click');
                 }
             }
-        // this.elements.volume.css('backgroundPosition','0px 0px !important')
 
-        },
-
-        _onPlay: function() {
-            this.fire('play');
-            return false;
-        },
+                    
+            //the function above calls map.add:
+            //add bindings when adding a marker:
+            map.bind('add',function(data){
+                mapuiAdd.apply(mapUI,[data.marker, data.index,data.isNew]);
+                rulerAdd.apply(ruler,[data.marker, data.index]);
+            });
 
-        _onPause: function() {
-            this.fire('pause');
-            return false;
-        },
+            //2) MOVE
 
-        _onSetMarker: function() {
-            if (this.map) {
-                this.fire('markeradd', {
-                    offset: this.getSoundPosition()
-                });
-            }
-            return false;
-        }
-    });
+            //add the binding when we move a marker on the ruler:
+            ruler.bind('markermoved',function(data){
+                var soundPos = data.soundPosition;
+                var markerClass = data.markerClass;
+                if(markerClass=='pointer'){
+                    player.setSoundPosition(soundPos);
+                }else{
+                    map.move(data.markerElement.getIndex(), soundPos);
+                }
+            });
+                    
+            //and now add a binding to the map when we move a marker:
+            var rulerMove = ruler.move;
+            var mapuiMove = mapUI.move;
+                   
+            map.bind('move', function(data){
+                var from = data.fromIndex;
+                var to = data.toIndex;
+                rulerMove.apply(ruler,[from,to]);
+                mapuiMove.apply(mapUI,[from,to,data.newOffset]);
+            });
+                    
+            //3) EVENTS ON MARKERDIV: SAVE AND REMOVE
+            //save - UI delegates the map:
+            var mapSave = map.save;
+            mapUI.bind('save',function(data){
+                mapSave.apply(map,[data.marker]);
+            });
+            //and map delegates back to the UI:
+            var mapuiSetEditMode = mapUI.setEditMode;
+            map.bind('save',function(data){
+                mapuiSetEditMode.apply(mapUI,[data.index,false]);
+            });
 
-    $N.notifyScriptLoad();
+            //remove - UI delegates the map:
+            var mapRemove = map.remove;
+            mapUI.bind('remove',function(data){
+                mapRemove.apply(map,[data.marker]);
+            });
+            //and, again, map delegates back to the UIs:
+            var mapuiRemove = mapUI.remove;
+            var rulerRemove = ruler.remove;
+            map.bind('remove',function(data){
+                mapuiRemove.apply(mapUI, [data.index]);
+                rulerRemove.apply(ruler, [data.index]);
+            });
 
-});
+            jQuery('#loading_span').empty().remove();
+            //TODO: move this in load_player?
+            //                    setUpPlayerTabs([jQuery('#tab_analysis'), jQuery('#tab_markers')],
+            //                    [jQuery('#analyzer_div_id'), jQuery('#markers_div_id')], tabIndex,
+            //                    'tab_selected','tab_unselected');
+            setUpPlayerTabs(jQuery('#tab_analysis').add(jQuery('#tab_markers')),
+                [jQuery('#analyzer_div_id'), jQuery('#markers_div_id')], tabIndex,
+                'tab_selected','tab_unselected');
+        };
+        json([itemId],"telemeta.get_markers", onSuccess);
+    }
+});
\ No newline at end of file
index bb89e287313af67b67790530e925a9ceb2f8df46..0cad033858c016df70ae353591132eb8d4b8ce1b 100644 (file)
-/**
- * TimeSide - Web Audio Components
- * Copyright (c) 2008-2009 Samalyse
- * Author: Olivier Guilyardi <olivier samalyse com>
- * License: GNU General Public License version 2.0
- */
-
-TimeSide(function($N, $J) {
-
-    $N.Class.create("Ruler", $N.Core, {
-
-        fullSectionDuration: 60,
-        sectionSubDivision: 10,
-        sectionSteps: [[5, 1], [10, 1], [20, 2], [30, 5], [60, 10], [120, 20], [300, 30],
-        [600, 60], [1800, 300], [3600, 600]],
-        sectionsNum: 0,
-        timeLabelWidth: 0,
-        pointerPos: 0,
-        layout: null,
-        width: null,
-        mouseDown: false,
-        pointer: null,
-        markers: new Array(),
-        duration: 0,
-        container: null,
-        waveContainer: null,
-
-        initialize: function($super, cfg) {
-            $super();
-            this.configure(cfg, {
-                viewer: [null, 'required'],
-                fontSize: 10,
-                //map: null,
-                sound: [null, 'required'],
-                soundDurationInMsec:0
-            });
-            this.cfg.viewer = $J(this.cfg.viewer);
-            this.container = this.cfg.viewer.find('.' + $N.cssPrefix + 'ruler');
-            this.waveContainer = this.cfg.viewer.find('.' + $N.cssPrefix + 'image-canvas');
-
-            //this.duration = this.cfg.sound.duration/1000; //TODO: improve this function!!
-            //note that soundmanager2 returns the duration in milliseconds, while here we compute the
-            //layout according to the duration in seconds. Changing all functions it's a pain and it's useless'
-
-            //initialize duration. If sound autoLoad=false, duration is zero and we must use durationEstimate
-            //this.duration = this.cfg.sound.duration ? this.cfg.sound.duration : this.cfg.sound.durationEstimate;
-            //this
-            this.duration = this.cfg.soundDurationInMsec/1000;
-            consolelog('duration - - '+this.cfg.sound.duration);
-            consolelog('duration -E- '+this.cfg.sound.bytesTotal);
-            
-            var imgContainer = this.cfg.viewer.find('.' + $N.cssPrefix + 'image-container'); // for IE
-            
-            this._observeMouseEvents(this.waveContainer.add(imgContainer));
 
-             //this is a workaround: when moving the marker the first time sound.setPosition seems not to work
-             //after playing the first time, it works. Or after having set position explicitly, apparently
-             //this.cfg.sound.setPosition(0);
-        //            this.sp = this._setPosition;
-            
-        //            this.cfg.sound.whileplaying(function(){
-        //                sp(this.position/1000);
-        //            });
-        //            this.cfg.sound.onfinish(function(){ //when it reaches the end (naturally) force pointer to be at the end
-        //                sp(this.duration);
-        //            });
-
-        //if (this.cfg.map) {
-        //    this.cfg.map
-        //.observe('add', this.attach(this._onMapAdd))
-        //.observe('remove', this.attach(this._onMapRemove))
-        //.observe('indexchange', this.attach(this._onMapIndexChange));
-        //}
-            
-        //this.cfg.soundProvider.observe('update', this.attach(this._onSoundProviderUpdate));
-        //this.cfg.soundProvider.observe('play', this.attach(this._onSoundProviderPlaying));
-        },
-
-        free: function($super) {
-            this.layout = null;
-            this.container = null;
-            this.waveContainer = null;
-            this.cfg.viewer = null;
-            $super();
-        },
-
-        _computeLayout: function() {
-            this.width = this.waveContainer.width();
-            this.debug('container width: ' + this.width);
-            var i, ii = this.sectionSteps.length;
-            this.timeLabelWidth = this._textWidth('00:00', this.cfg.fontSize);
-            for (i = 0; i < ii; i++) {
-                //                this.debug('step: ' +i+' duration: '+this.sectionSteps[i][0]);
-                //                this.debug('step: ' +i+' subdivision: '+this.sectionSteps[i][1]);
-                //                this.debug('labelsNum: ' +i+' labelsNum (this.duration/duration): '+Math.floor(this.duration / duration));
-
-                var duration = this.sectionSteps[i][0];
-                var subDivision = this.sectionSteps[i][1];
-                var labelsNum = Math.floor(this.duration / duration);
-                if ((i == ii - 1) || (this.width / labelsNum > this.timeLabelWidth * 2)) {
-                    this.fullSectionDuration = duration;
-                    this.sectionSubDivision = subDivision;
-                    this.sectionsNum = Math.floor(this.duration / this.fullSectionDuration);
-                    this.debug('(in _computeLayout) this.fullSectionDuration: ' + this.fullSectionDuration);
-                    this.debug('(in _computeLayout) sectionsNum: ' +this.sectionsNum);
-                    this.debug('(in _computeLayout) duration: ' +this.duration);
-                    break;
-                }
+var Ruler = TimesideArray.extend({
+    //init constructor: soundDuration is IN SECONDS!!! (float)
+    init: function(viewer, soundDuration, isInteractive){
+        this._super();
+        var cssPref = this.cssPrefix;
+        
+        this.isInteractive = function(){
+            return isInteractive;
+        };
+        
+        this.getSoundDuration= function(){
+            return soundDuration;
+        };
+        
+        var waveContainer = viewer.find('.' + cssPref + 'image-canvas');
+        this.debug( 'WAVECONTAINER?? LENGTH='+ waveContainer.length);
+        this.getWaveContainer =function(){
+            return waveContainer;
+        };
+        //ts-image-canvas has width=0. Why was not the case in old code?
+        //BECAUSE IN OLD CODE ts-image-canvas has style="width..height" defined, and not HERE!!!!
+        this.getContainerWidth =function(){
+            return waveContainer.width();
+        };
+        
+        
+        this.debug( 'init ruler: container width '+this.getContainerWidth());
+        
+        
+        //private function used in resize() defined below
+        
+
+        var container = viewer.find('.' + cssPref + 'ruler');
+        
+        this.getRulerContainer = function(){
+            return container;
+        }
+
+
+        if(!isInteractive){ //is not interactive, skip all methods assignmenets below
+            return;
+        }
+
+    // TODO: check here
+    // http://stackoverflow.com/questions/3299926/ie-mousemove-bug
+    // div in IE to receive mouse events must have a background
+    // so for the moment
+        
+        
+   
+    //        var mouseDown = false;
+    //        var _onMouseDown = function(evt) {
+    //            mouseDown = true;
+    //            this._onMouseMove(evt);
+    //            evt.preventDefault(); //If this method is called, the default action of the event will not be triggered.
+    //        };
+    //        var _onMouseMove = function(evt) {
+    //            if (mouseDown) {
+    //                var pixelOffset = evt.pageX - container.offset().left;
+    //                this._movePointerAndUpdateSoundPosition(pixelOffset / this.width * this.duration);
+    //            //moves the pointer and fires onPointerMove
+    //            }
+    //            return false;
+    //        };
+    //
+    //        var _onMouseUp= function(evt) {
+    //            if (mouseDown) {
+    //                mouseDown = false;
+    //                this.debug('_onMouseUp:'+this.pointerPos+' '+this.cfg.sound.position);
+    //            }
+    //            return false;
+    //        };
+    //        var imgContainer = viewer.find('.' + cssPref + 'image-container'); // for IE
+    //        var element = waveContainer.add(imgContainer); //constructs a new jQuery object which is the union of the jquery objects
+    //
+    //        element
+    //        .bind('click dragstart', function() {
+    //            return false;
+    //        })
+    //        .bind('mousedown', function(evt){
+    //            return _onMouseDown(evt);
+    //        })
+    //        .bind('mousemove', function(evt){
+    //            return _onMouseMove(evt);
+    //        })
+    //        .bind('mouseup', function(evt){
+    //            return _onMouseUp(evt);
+    //        });
+    //        this.$J(document)
+    //        .bind('mousemove', function(evt){
+    //            return _onMouseMove(evt);
+    //        });
+
+    },
+
+    resize : function(){
+        //code copied from old implementation, still to get completely what is going on here...
+        var sectionSteps = [[5, 1], [10, 1], [20, 2], [30, 5], [60, 10], [120, 20], [300, 30],
+        [600, 60], [1800, 300], [3600, 600]];
+        //old computeLayout code
+        var fullSectionDuration,sectionSubDivision, sectionsNum;
+        var width = this.getContainerWidth();
+        var duration = this.getSoundDuration();
+        var cssPref = this.cssPrefix;//defined in superclass
+        var fontSize = 10;
+        var mfloor = Math.floor; //instanciating once increases performances
+        var $J = this.$J; //reference to jQuery
+        //this.debug('container width: ' +" "+width);
+
+
+        var i, ii = sectionSteps.length;
+        var timeLabelWidth = this._textWidth('00:00', fontSize);
+        for (i = 0; i < ii; i++) {
+            var tempDuration = sectionSteps[i][0];
+            var subDivision = sectionSteps[i][1];
+            var labelsNum = mfloor(duration / tempDuration);
+            if ((i == ii - 1) || (width / labelsNum > timeLabelWidth * 2)) {
+                fullSectionDuration = tempDuration;
+                sectionSubDivision = subDivision;
+                sectionsNum = mfloor(duration / fullSectionDuration);
+                //this.debug('(in _computeLayout) this.fullSectionDuration: ' + fullSectionDuration);
+                //this.debug('(in _computeLayout) sectionsNum: ' +sectionsNum);
+                //this.debug('(in _computeLayout) sectionSubDivision: ' +sectionSubDivision);
+                break;
             }
-        },
+        }
+        //old draw() code:
+        if (!duration) {
+            this.debug("Can't draw ruler with a duration of 0");
+            return;
+        }
+        //this.debug("draw ruler, duration: " + duration);
+
+        var container = this.getRulerContainer();
+        var layout = container.find("."+cssPref + 'layout');
+        //REDONE: if does not exists, create it
+        if(!layout || !(layout.length)){
+            layout = $J('<div/>')
+            .addClass(cssPref + 'layout')
+            .css({
+                position: 'relative'
+            }) // bugs on IE when resizing
+            //TODO: bind doubleclick events!!!!!!
+            //.bind('dblclick', this.attachWithEvent(this._onDoubleClick))
+            //.bind('resize', this.attachWithEvent(this.resize)) // Can loop ?
+            .appendTo(container);
+        }else{
+            //remove all elements neither pointer nor marker
+            layout.find(':not(a.ts-pointer,a.ts-marker,a.ts-pointer>*,a.ts-marker>*)').remove();
+        }
+
+        //        if (layout && layout.length){
+        //            layout.remove();
+        //        }
+        //        layout = $J('<div/>')
+        //        .addClass(cssPref + 'layout')
+        //        .css({
+        //            position: 'relative'
+        //        }) // bugs on IE when resizing
+        //        //TODO: bind doubleclick events!!!!!!
+        //        //.bind('dblclick', this.attachWithEvent(this._onDoubleClick))
+        //        //.bind('resize', this.attachWithEvent(this.resize)) // Can loop ?
+        //        .appendTo(container);
 
-        getUnitDuration: function() {
-            return this.sectionSubDivision;
-        },
+        
 
-        resize: function() {
-            //            var pointerVisible = this.pointer && this.pointer.isVisible();
-            //            this.debug('resizing (pointer visible: :'+pointerVisible+':');
-            //            alert(this.pointer.isVisible());
-            this._computeLayout();
+        //creating sections
+        //defining function maketimelabel
+        var makeTimeLabel = this.makeTimeLabel;
             
-            this.draw();
-            if(this.pointer){
-                if(!this.pointer.isVisible()){
-                    this.pointer.show();
-                }
-                //            }
-                //            if (pointerVisible) {
-                //                this.setPosition(this.cfg.soundProvider.getPosition());
-                //                this.setBuffering(this.cfg.soundProvider.isBuffering() && this.cfg.soundProvider.isPlaying());
-
-                this._movePointer(this.cfg.sound.position/1000);
-                this.setBuffering(this.cfg.sound.isBuffering && this.cfg.sound.playState==1);
-            //Note that playState =  1 may not always guarantee that sound is being heard, given buffering and autoPlay status.
-            //(from soundmanager2 tutorial)
-            }
-        },
-
-//        _setDuration: function(duration) {
-//            this.debug('duration setting ruler: ' + duration);
-//            this.duration = duration;
-//            this._computeLayout();
-//        },
-//
-//        setDuration: function(durationInMillisecs) {
-//            var duration = durationInMillisecs ? durationInMillisecs/1000 : 60;
-//            if (this.duration != duration) {
-//                this._setDuration(duration);
-//                this.draw();
-//            }
-//        },
-
-        _createSection: function(timeOffset, pixelWidth) {
+        //defining the function createSection
+        var _createSection = function(timeOffset, pixelWidth,timeLabelWidth) {
             var section = $J('<div/>')
-            .addClass($N.cssPrefix + 'section')
+            .addClass(cssPref + 'section')
             .css({
-                fontSize: this.cfg.fontSize + 'px',
+                fontSize: fontSize + 'px',
                 fontFamily: 'monospace',
                 width: pixelWidth,
                 overflow: 'hidden'
             })
-            .append($J('<div />').addClass($N.cssPrefix + 'canvas'));
+            .append($J('<div />').addClass(cssPref + 'canvas'));
 
             var topDiv = $J('<div/>')
-            .addClass($N.cssPrefix + 'label')
+            .addClass(cssPref + 'label')
             .appendTo(section);
             var bottomDiv = $J('<div/>')
-            .addClass($N.cssPrefix + 'lines')
+            .addClass(cssPref + 'lines')
+                
             .appendTo(section);
             var empty = $J('<span/>').css({
                 visibility: 'hidden'
             }).text('&nbsp;');
-            if (pixelWidth > this.timeLabelWidth) {
-                var text = $J('<span/>')
-                .text($N.Util.makeTimeLabel(timeOffset))
-                .bind('mousedown selectstart', function() {
+            var text;
+
+            if (pixelWidth > timeLabelWidth) {
+                text = $J('<span/>')
+                .text(makeTimeLabel(timeOffset))
+                .bind('mousedown selectstart', function() { //WHY THIS?
                     return false;
                 });
             } else {
-                var text = empty.clone();
+                text = empty.clone();
             }
             topDiv.append(text);
             bottomDiv.append(empty);
             return section;
-        },
+        };
+        //function defined, creating sections:
+        var sections = new Array();
+        var currentWidth = 0;
+        var sectionDuration, sectionWidth;
+        for (i = 0; i <= sectionsNum; i++) {
+            if (i < sectionsNum) {
+                sectionDuration = fullSectionDuration;
+                sectionWidth = mfloor(sectionDuration / duration * width);
+            } else {
+                sectionDuration = duration - i * fullSectionDuration;
+                sectionWidth = width - currentWidth;
+
+            }
+            var section = _createSection(i * fullSectionDuration, sectionWidth, timeLabelWidth);
+            if (i > 0) {
+                section.css({
+                    left: currentWidth,
+                    top: 0,
+                    position: 'absolute'
+                });
+            }
+            section.duration = sectionDuration;
+            layout.append(section);
+            currentWidth += section.width();
+            sections[i] = section;
+        }
 
-        _drawSectionRuler: function(section, drawFirstMark) {
+        //function to draw section rulers:
+        var _drawSectionRuler= function(section, drawFirstMark) {
             var j;
-            var jg = new jsGraphics(section.find('.' + $N.cssPrefix + 'canvas').get(0));
-            jg.setColor(this.layout.find('.' + $N.cssPrefix + 'lines').css('color'));
+               
+            var jg = new jsGraphics(section.find('.' + cssPref + 'canvas').get(0));
+            jg.setColor(layout.find('.' + cssPref + 'lines').css('color'));
             var height = section.height();
             var ypos;
-            for (j = 0; j < section.duration; j += this.sectionSubDivision) {
+            for (j = 0; j < section.duration; j += sectionSubDivision) {
                 if (j == 0) {
                     if (drawFirstMark) {
                         ypos = 0;
@@ -200,325 +251,275 @@ TimeSide(function($N, $J) {
                 } else {
                     ypos = (j == section.duration / 2) ? 1/2 + 1/8 : 3/4;
                 }
-                var x = j / this.duration * this.width;
+                //var x = j / this.duration * this.width;
+                var x = j / duration * width;
                 jg.drawLine(x, height * ypos, x, height - 1);
             }
             jg.paint();
-        },
-
-        getHeight: function() {
-            return this.container.find('' + $N.cssPrefix + '.section').height();
-        },
-
-        draw: function() {
-            if (!this.duration) {
-                this.debug("Can't draw ruler with a duration of 0");
-                return;
-            }
-            this.debug("draw ruler, duration: " + this.duration);
-            if (this.layout){
-                this.layout.remove();
-            }
-            this.layout = $J('<div/>')
-            .addClass($N.cssPrefix + 'layout')
-            .css({
-                position: 'relative'
-            }) // bugs on IE when resizing
-            .bind('dblclick', this.attachWithEvent(this._onDoubleClick))
-            //.bind('resize', this.attachWithEvent(this.resize)) // Can loop ?
-            .appendTo(this.container);
-
-            //this.container.html(this.layout);
-
-            var sections = new Array();
-            var currentWidth = 0;
-            var i;
-            for (i = 0; i <= this.sectionsNum; i++) {
-                if (i < this.sectionsNum) {
-                    var duration = this.fullSectionDuration;
-                    var width = Math.floor(duration / this.duration * this.width);
-                } else {
-                    var duration = this.duration - i * this.fullSectionDuration;
-                    var width = this.width - currentWidth;
-
-                }
-                var section = this._createSection(i * this.fullSectionDuration, width);
-                if (i > 0) {
-                    section.css({
-                        left: currentWidth,
-                        top: 0,
-                        position: 'absolute'
-                    });
-                }
-                section.duration = duration;
-                this.layout.append(section);
-                currentWidth += section.width();
-                sections[i] = section;
-            }
+        };
+        //draw section rulers
+        for (i = 0; i <= sectionsNum; i++) {
+            _drawSectionRuler(sections[i], (i > 0));
+        }
 
-            for (i = 0; i <= this.sectionsNum; i++) {
-                this._drawSectionRuler(sections[i], (i > 0));
-            }
+       
+        var pointer = undefined;
+        if('getPointer' in this){
+            pointer = this.getPointer();
+        }
+        if(!pointer){
+            //consolelog('QUALE CHAZZO E IL CONTAINER??????                  ' + $J(layout.get(0)).attr('class'));
+            //            pointer = new RulerMarker($J(layout.get(0)),this.getWaveContainer(),'pointer', true);
+            //            pointer.setText(this.makeTimeLabel(0));
+            //
+            //            this.debug('WELL, ');
+            //            consolelog(pointer);
+            //            var me = this;
+            //            pointer.getLabel().mousedown(function(evt) {
+            //                var lbl = $J(evt.target);
+            //                me.markerBeingClicked = {
+            //                    'marker':pointer,
+            //                    'offset':evt.pageX-(lbl.offset().left+lbl.outerWidth(true)/2)
+            //                };
+            //                consolelog(evt.pageX-(lbl.offset().left+lbl.outerWidth(true)/2));
+            //                evt.stopPropagation(); //dont notify the ruler;
+            //                return false;
+            //            });
+            pointer = this.add(0);
+            this.getPointer = function(){
+                return pointer;
+            };
+        }else{
+            pointer.refreshPosition();
+            
+        }
+        this.each(function(i,rulermarker){
+            rulermarker.refreshPosition();
+        });
+
+    //            if(!pointer){
+    //                this.debug("Creating pointer:"+layout);
+    //                //this.createMarkerForRuler = function(rulerLayout,viewer,className, fontSize, optionalToolTip)
+    //                pointer = this.createMarkerForRuler($J(layout.get(0)),waveContainer,'pointer',fontSize,'move pointer');
+    //                this.debug('pointerdisplay'+pointer.css('display'));
+    //            }
+
+    //TODO: move pointer??????
+    //this._movePointer(sound.position/1000);
+
+
+    //TODO: draw markers?
+    //            if (this.cfg.map) {
+    //                $J(this.markers).each(function(i, m) {
+    //                    m.clear();
+    //                });
+    //                this.markers = new Array();
+    //                this.cfg.map.each(this.attach(function(i, m) {
+    //                    this.markers.push(this._drawMarker(m, i));
+    //                }));
+    //            }
+    },
 
-            this._createPointer();
-            //draw markers
-            if (this.cfg.map) {
-                $J(this.markers).each(function(i, m) {
-                    m.clear();
-                });
-                this.markers = new Array();
-                this.cfg.map.each(this.attach(function(i, m) {
-                    this.markers.push(this._drawMarker(m, i));
-                }));
-            }
-        //this._drawMarkers();
-        },
-
-        //        _drawMarkers: function() {
-        //            if (this.cfg.map) {
-        //                $J(this.markers).each(function(i, m) {
-        //                    m.clear();
-        //                });
-        //                this.markers = new Array();
-        //                this.cfg.map.each(this.attach(function(i, m) {
-        //                    this.markers.push(this._drawMarker(m, i));
-        //                }));
-        //            }
-        //        },
-
-        _createPointer: function() {
-            if (this.pointer) {
-                this.pointer.clear();
-            }
-            this.pointer = new $N.RulerMarker({
-                rulerLayout: this.layout.get(0),
-                viewer: this.waveContainer,
-                fontSize: this.cfg.fontSize,
-                zIndex: 1000,
-                top:0,
-                className: 'pointer',
-                tooltip: 'Move head',
-                canMove: true
+    //overridden: Note that the pointer is NOT cleared!!!!!
+    clear: function(){
+        var markers = this._super();
+        //        if('getPointer' in this){
+        //            markers.push(this.getPointer());
+        //        }
+        for( var i=0; i<markers.length; i++){
+            markers[i].remove();
+        }
+        return markers;
+    },
+    //overridden TimesideArray methods (add, move, remove):
+    remove: function(index){
+        var rulermarker = this._super(index);
+        rulermarker.remove();
+        this.each(index, function(i,rulermarker){
+            consolelog(i);
+            rulermarker.setIndex(i, true);
+        });
+    },
+    //overridden
+    move: function(from, to){
+        var newIndex = this._super(from,to);
+        //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;
+            //this.debug('updating ['+i1+','+i2+']');
+            this.each(i1,i2, function(index,rulermarker){
+                rulermarker.setIndex(index, true);
             });
-            //            //create the label
-            //            var tsMainLabel = $.find('.' + $N.cssPrefix + 'label');
-            //            if(tsMainLabel){
-            //                var label = tsMainLabel.find('#' + $N.cssPrefix + 'pointerOffset');
-            //                if(!label){
-            //                    label = $("<span/>").id('#' + $N.cssPrefix + 'pointerOffset').css('zIndex','10').appendTo(tsMainLabel);
-            //                    this.pointer.label = label;
-            //                }
-            //            }
-
-            this.pointer
-            //.setText("+")
-            .setText($N.Util.makeTimeLabel(0))
-            .observe('move', this.attach(this._onPointerMove));
-        },
-
-        //        _setPosition: function(offset) {
-        //            this._movePointer(offset);
-        ////            if (this.pointer) {
-        ////                this.pointer.show();
-        ////            }
-        //        },
-        
-    
-
+        }
+    },
+    //overridden
+    //markerObjOrOffset can be a marker object (see in markermap) or any object with the fields isEditable and offset
+    add: function(markerObjOrOffset, indexIfMarker){
+        var soundPosition;
+        var isMovable;
+        var markerClass;
+
+        if(typeof markerObjOrOffset == 'number'){
+            soundPosition = markerObjOrOffset;
+            isMovable = this.isInteractive();
+            markerClass='pointer';
+        }else{
+            soundPosition = markerObjOrOffset.offset;
+            isMovable = markerObjOrOffset.isEditable && this.isInteractive();
+            markerClass='marker';
+        }
         
+        var container = this.getRulerContainer();
+        var layout = container.find("."+this.cssPrefix + 'layout');
+        var $J = this.$J;
+        var pointer = new RulerMarker($J(layout.get(0)),this.getWaveContainer(),markerClass);
+        //call super constructor
+        //if it is a pointer, dont add it
+        if(markerClass != 'pointer'){
+            this._super(pointer,indexIfMarker); //add at the end
+            //note that setText is called BEFORE move as move must have the proper label width
+            this.each(indexIfMarker, function(i,rulermarker){
+                rulermarker.setIndex(i,i!=indexIfMarker);
+                //rulermarker.setIndex.apply(rulermarker, [i,i!=indexIfMarker]); //update label width only if it is not this marker added
+                //as for this marker we update the position below (move)
+            });
+            this.debug('added marker at index '+indexIfMarker+' offset: '+markerObjOrOffset.offset);
+        }else{
+            //note that setText is called BEFORE move as move must have the proper label width
+            pointer.setText(this.makeTimeLabel(0));
+        }
+        //proceed with events and other stuff: move (called AFTER setText or setText)
+        pointer.move(this.toPixelOffset(soundPosition));
+       
+        //pointer.setText(markerClass== 'pointer' ? this.makeTimeLabel(0) : this.length);
 
-        //        setPosition: function(offset) {
-        //            if (!this.mouseDown) {
-        //                this._setPosition(offset);
-        //            }
-        //        },
-
-        //        shiftPosition: function(delta) {
-        //            this.setPosition(this.pointerPos + delta);
-        //        },
-
-        hidePointer: function() {
-            if (this.pointer)
-                this.pointer.hide();
-        },
+        //if there are no events to associate, return it.
+        if(!isMovable){
+            return pointer;
+        }
 
-        setBuffering: function(state) {
-            if (this.pointer) {
-                this.pointer.blink(state);
-            }
-        },
-        /*
-    _onClick: function(evt) {
-        var offset = (evt.pageX - this.container.offset().left) 
-            / this.width * this.duration;
-        this._setPosition(offset);
-        this.fire('move', {offset: offset});
-    },
-*/
-        _onMouseDown: function(evt) {
-            this.mouseDown = true;
-            this._onMouseMove(evt);
-            evt.preventDefault(); //If this method is called, the default action of the event will not be triggered.
-        },
+        //namespace for jquery event:
+        var eventId = 'markerclicked';
+        var doc = $J(document);
+        var lbl = pointer.getLabel();
+        
+        var me = this;
 
-       
-        _onMouseMove: function(evt) {
-            if (this.mouseDown) {
-                var pixelOffset = evt.pageX - this.container.offset().left;
-                this._movePointerAndUpdateSoundPosition(pixelOffset / this.width * this.duration);
-                //moves the pointer and fires onPointerMove
-                return false;
-            }
-        },
+        var ismovingpointer = false;
+        var setmovingpointer = function(value){
+            ismovingpointer = value;
+        }
+        //TODO: this method below private, but how to let him see in the bind below???
+        this.setPointerMovingFromMouse = function(value){setmovingpointer(value);}
+        this.isPointerMovingFromMouse = function(){ return ismovingpointer;};
+        //functions to set if we are moving the pointer (for player when playing)
 
-        _onMouseUp: function(evt) {
-            if (this.mouseDown) {
-                this.mouseDown = false;
-                this.debug('_onMouseUp:'+this.pointerPos+' '+this.cfg.sound.position);
-                //this.debug("mousedup"+this.cfg.sound.position)
-            }
-            return false;
-        },
-        //called while playing, does not update sound position
-        _movePointer: function(offset) {
+        lbl.bind('mousedown.'+eventId,function(evt) {
             
-            if (offset < 0){
-                offset = 0;
-            }else if (offset > this.duration){
-                offset = this.duration;
-            }
-            var pixelOffset = offset / this.duration * this.width;
-            if (this.pointer) {
-                this.pointer.move(pixelOffset); //does NOT fire any move method
-                this.pointer.setText($N.Util.makeTimeLabel(offset));
+            if(markerClass=='pointer'){
+                me.setPointerMovingFromMouse(true);
             }
-            this.pointerPos = offset;
-            this.debug('_movePointer: position set to'+offset);
-        },
-        //called by everything else than playing, same as _movePointer but updates also the sound position accordingly
-        _movePointerAndUpdateSoundPosition: function(offset) {
-            this._movePointer(offset);
-            this.cfg.sound.setPosition(parseInt(1000*this.pointerPos));
-        },
-
-         _onPointerMove: function(evt, data) {
-            //this.debug('_onPointerMove:'+ this.pointerPos+' '+this.cfg.sound.position);
-
-            this.mouseDown = true;
-            this._movePointerAndUpdateSoundPosition(data.offset / this.width * this.duration);
-            if(data.finish) {
-                //                this.fire('move', {
-                //                    offset: this.pointerPos
-                //                });
-                this.mouseDown = false;
-            }
-            return false;
-        },
 
-        
-        _observeMouseEvents: function(element) {
-            if(!(CURRENT_USER_NAME)){
-                return;
-            }
-            element
-            .bind('click dragstart', function() {
-                return false;
-            })
-            .bind('mousedown', this.attachWithEvent(this._onMouseDown))
-            .bind('mousemove', this.attachWithEvent(this._onMouseMove))
-            .bind('mouseup', this.attachWithEvent(this._onMouseUp));
-            $J(document)
-            .bind('mousemove', this.attachWithEvent(this._onMouseMove));
-        },
-
-        _drawMarker: function(marker, index) {
-            if (marker.offset < 0){
-                marker.offset = 0;
-            }else if (marker.offset > this.duration){
-                marker.offset = this.duration;
-            }
-            
-            pixelOffset = marker.offset / this.duration * this.width;
+            var startX = evt.pageX; //lbl.position().left-container.position().left;
+            var startPos = lbl.position().left+lbl.width()/2;
             
-            m = new $N.RulerMarker({
-                rulerLayout: this.layout.get(0),
-                viewer: this.waveContainer,
-                fontSize: this.cfg.fontSize,
-                className: 'marker',
-                index: index,
-                tooltip: 'Move marker',
-                canMove: marker.isEditable
+            evt.stopPropagation(); //dont notify the ruler;
+            var newPos = startPos;
+            doc.bind('mousemove.'+eventId, function(evt){
+                var x = evt.pageX; 
+                newPos = startPos+(x-startX);
+                pointer.move(newPos);
+                //update the text if pointer
+                if(markerClass=='pointer'){
+                    pointer.setText(me.makeTimeLabel(me.toSoundPosition(newPos)));
+                }
+                return false;
+                
             });
-            
-            if(marker.isEditable){
-                m.observe('move', this.attach(this._onMarkerMove))
-            }
-            //m.observe('move', this.attach(this._onMarkerMove))
-            m
-            //.setText(index + 1)
-            .move(pixelOffset)
-            .show();
-            return m;
-        },
-
-        _onMarkerMove: function(e, data) {
-            if (data.finish) {
-                var offset = data.offset / this.width * this.duration;
-                this.fire('markermove', {
-                    index: data.index,
-                    offset: offset
-                });
-            }
-        },
-        
-        //called from markermap after we retrieved the marker index:
-        onMapAdd: function(marker, index){
-            this.markers.splice(index, 0, this._drawMarker(marker, index));
-        },
-
-        //        _onMapAdd2: function(e, data) {
-        //            this.markers.push(this._drawMarker(data.marker, data.index));
-        //        },
-
-        remove: function(index){
-            var rulermarker = this.markers[index];
-            rulermarker.clear();
-            this.markers.splice(index, 1);
-        },
+            lbl.bind('click.'+eventId, function(){
+                return false;
+            }); //to avoid scrolling
+            //TODO: what happens if the user releases the mouse OUTSIDE the browser????
+            var mouseup = function(evt_){
+                doc.unbind('mousemove.'+eventId);
+                doc.unbind('mouseup.'+eventId);
+                evt_.stopPropagation();
+                //TODO: fire event marker moved (with the class name)
+                var data = {
+                    'markerElement':pointer,
+                    'soundPosition': me.toSoundPosition.apply(me,[newPos]),
+                    'markerClass':markerClass
+                };
+                if(markerClass=='pointer'){
+                    me.setPointerMovingFromMouse(false);
+                }
+                me.fire('markermoved',data);
+                return false;
+            };
+            doc.bind('mouseup.'+eventId, mouseup);
+            //lbl.bind('mouseup.'+eventId, mouseup);
+            //            doc.bind('mouseup.'+eventId, function(evt){
+            //                consolelog(newPos);
+            //                doc.unbind('mousemove.'+eventId);
+            //                doc.unbind('mouseup.'+eventId);
+            //
+            //                //TODO: fire event marker moved (with the class name)
+            //                var data = {
+            //                    'markerElement':pointer,
+            //                    'soundPosition': me.toSoundPosition.apply(me,[newPos]),
+            //                    'markerClass':markerClass
+            //                };
+            //                me.fire('markermoved',data);
+            //                return false;
+            //            });
+            return false;
+        });
         
-        //it is assured that fromIndex!=toIndex and fromIndex!=toIndex+1 (see markermap.move)
-        //        move: function(fromIndex, toIndex){
-        //            var m = this.markers.splice(fromIndex,1)[0]; //remove
-        //            this.markers.splice(toIndex,0,m); //add
-        //        },
-
-        updateMarkerIndices:function(fromIndex, toIndex){
-            for(var i=fromIndex; i<=toIndex; i++){
-                this.markers[i].setIndex(i);
-            }
-        },
-
-        _onDoubleClick: function(evt) {
-            if (CURRENT_USER_NAME) {
-                var offset = (evt.pageX - this.container.offset().left)
-                / this.width * this.duration;
-                this.fire('markeradd', {
-                    offset: offset
-                });
-            }
-        }
+        return pointer;
 
-    //        , _onSoundProviderUpdate: function(e) {
-    //            this.debug("spupdate");
-    //
-    //            //this.setDuration(this.cfg.soundProvider.getDuration());
-    //            this.setPosition(this.cfg.soundProvider.getPosition());
-    //            this.setBuffering(this.cfg.soundProvider.isBuffering() && this.cfg.soundProvider.isPlaying());
-    //        }
-    });
 
-    $N.notifyScriptLoad();
+    },
 
-});
+    //moves the pointer, does not notify any listener.
+    //soundPosition is in seconds (float)
+    movePointer : function(soundPosition) {
+        var pointer = this.getPointer();
+        if (pointer) {
+            var pixelOffset = this.toPixelOffset(soundPosition);
+            //first set text, so the label width is set, then call move:
+            pointer.setText(this.makeTimeLabel(soundPosition));
+            pointer.move(pixelOffset); //does NOT fire any move method
+        }
+        //this.debug('moving pointer: position set to '+offset);
+        return soundPosition;
+    },
 
+    //soundPosition is in seconds (float)
+    toPixelOffset: function(soundPosition) {
+        //this.debug('sPos:' + soundPosition+ 'sDur: '+this.getSoundDuration());
+        var duration = this.getSoundDuration();
+        if (soundPosition < 0){
+            soundPosition = 0;
+        }else if (soundPosition > duration){
+            soundPosition = duration;
+        }
+        var width = this.getContainerWidth();
+        var pixelOffset = (soundPosition / duration) * width;
+        return pixelOffset;
+    },
+
+    //returns the soundPosition is in seconds (float)
+    toSoundPosition: function(pixelOffset) {
+        var width = this.getContainerWidth();
 
+        if (pixelOffset < 0){
+            pixelOffset = 0;
+        }else if (pixelOffset > width){
+            pixelOffset = width;
+        }
+        var duration = this.getSoundDuration();
+        var soundPosition = (pixelOffset / width) *duration;
+        return soundPosition;
+    }
+});
\ No newline at end of file
index 3304f4ecc8f7fdcaca6759bde8377c47f168d61f..6e516074fdca0a6141b65d92a840184869d14b7c 100644 (file)
-/**
- * TimeSide - Web Audio Components
- * Copyright (c) 2008-2009 Samalyse
- * Author: Olivier Guilyardi <olivier samalyse com>
- * License: GNU General Public License version 2.0
- */
-
-TimeSide(function($N, $J) {
-
-    $N.Class.create("RulerMarker", $N.Core, {
-        id: null,
-        painter: null,
-        visible: false,
-        position: 0,
-        label: null,
-        blinking: false,
-        nodes: null,
-        mouseDown: false,
-        blinkAnimation: null,
-        mouseDownOffset:0,
-
-        initialize: function($super, cfg) {
-            $super();
-            //sets the fields required???? see ruler.js createPointer
-            this.configure(cfg, {
-                rulerLayout: [null, 'required'],
-                viewer: [null, 'required'],
-                fontSize: 10,
-                zIndex: null,
-                className: [null, 'required'],
-                index: null,
-                tooltip: null,
-                canMove: false
-            });
-            this.cfg.rulerLayout = $J(this.cfg.rulerLayout);
-            this.cfg.viewer = $J(this.cfg.viewer);
-
-            this.width = this.cfg.viewer.width();
-            this.painter = new jsGraphics(this.cfg.viewer.get(0));
-            this._create();
-            if(this.cfg.canMove){
-                this._observeMouseEvents();
-            }
-            //if it is the pointer, cfg.index is undefined
-            if(cfg.index !== undefined && cfg.className!='pointer'){
-                this.setIndex(cfg.index);
-            }
-            
-        },
-
-        setIndex: function(index){
-            this.index = index;
-            this.setText(index+1);
-        },
-
-        free: function($super) {
-            this.cfg.rulerLayout = null;
-            this.cfg.viewer = null;
-            $super();
-        },
-
-        clear: function() {
-            this.painter.clear();
-            $J(this.painter.cnv).remove();
-            this.label.remove();
-            return this;
-        },
-
-        _create: function() {
-            this.debug('create marker');
-            var y = this.cfg.rulerLayout.find('.' + $N.cssPrefix + 'label').outerHeight();
-            //added by me:================
-            if(this.cfg.className == "pointer"){
-                y = 0;
-            }
-            //==========================
-            this.label = $J('<a/>')
-            .css({
-                display: 'block',
-                width: '10px', 
-                textAlign: 'center', 
-                position: 'absolute', 
-                fontSize: this.cfg.fontSize + 'px', 
-                fontFamily: 'monospace', 
-                top: y + 'px'
-            })
-            .attr('href', '#')
-            .addClass($N.cssPrefix + this.cfg.className)
-            .append('<span />')
-            .hide();
-
-            if (this.cfg.tooltip){
-                this.label.attr('title', this.cfg.tooltip);
-            }
-            
-            this.cfg.rulerLayout.append(this.label);
-
-            var height = this.cfg.viewer.height();
-            var x = 0;
-            this.painter.drawLine(x, 0, x, height);
-            x     = [-4, 4, 0];
-            y = [0, 0, 4];
-            
-            this.painter.fillPolygon(x, y);
-            this.painter.paint();
-            this.nodes = $J(this.painter.cnv).children();
-
-            var style = {};
-            if (this.cfg.zIndex) {
-                style.zIndex = this.cfg.zIndex;
-                this.label.css(style);
-            }
-            style.backgroundColor = '';
-
-            this.nodes.hide().css(style).addClass($N.cssPrefix + this.cfg.className)
-            .each(function(i, node) {
-                node.originalPosition = parseInt($J(node).css('left'));
-            });
-          
-        },
+var RulerMarker = TimesideClass.extend({
+
+    init: function(rulerLayout, viewer, className) {
+        this._super();
+        var $J = this.$J;
+        var fontSize = 10;
+        this.getFontSize = function(){
+            return fontSize;
+        }
+        var zIndex = 1000;
+        var tooltip = '';
+        //TODO: why viewer get(0) ? more than one? check and maybe simplify
+        var painter = new jsGraphics(viewer.get(0));
+        //from create (oldCode)
+        var cssPref = this.cssPrefix;
+        var y = rulerLayout.find('.' + cssPref + 'label').outerHeight();
+        //added by me:================
+        if(className == "pointer"){
+            y = 0;
+        }
+        //==========================
+        var label = $J('<a/>')
+        .css({
+            display: 'block',
+            width: '10px',
+            textAlign: 'center',
+            position: 'absolute',
+            fontSize: fontSize + 'px',
+            fontFamily: 'monospace',
+            top: y + 'px'
+        })
+        .attr('href', '#')
+        .addClass(cssPref + className)
+        .append('<span />')
+        //.hide();
+
+        if (tooltip){
+            label.attr('title', tooltip);
+        }
 
-        setText: function(text) {
-            if (this.label) {
-                text += '';
-                var labelWidth = this._textWidth(text, this.cfg.fontSize) + 10;
-                labelWidth += 'px';
-                if (this.label.css('width') != labelWidth) {
-                    this.label.css({
-                        width: labelWidth
-                    });
-                }
-                this.label.find('span').html(text);
-            }
-            return this;
-        },
+        rulerLayout.append(label);
+        
+        var height = viewer.height();
+        var x = 0;
+        painter.drawLine(x, 0, x, height);
+        
+        x     = [-4, 4, 0];
+        y = [0, 0, 4];
+
+        painter.fillPolygon(x, y);
+        painter.paint();
+        var nodes = $J(painter.cnv).children();
+
+        var style = {};
+        if (zIndex) {
+            style.zIndex = zIndex;
+            label.css(style);
+        }
+        style.backgroundColor = '';
+        //nodes.hide();
+        nodes.css(style).addClass(cssPref + className)
+        .each(function(i, node) {
+            node.originalPosition = parseInt($J(node).css('left'));
+        });
+
+        //set the index,
+        var index = -1;
+        this.setIndex = function(idx, optionalUpdateLabelWidth){
+            index = idx;
+            this.setText(idx+1, optionalUpdateLabelWidth ? true : false);
+        };
+        this.getIndex = function(){
+            return index;
+        }
 
-        move: function(pixelOffset) {
-            if (this.position != pixelOffset) {
+        //end=======================================================
+        //creating public methods:
+        this.getLabel = function(){
+            return label;
+        }
+        
+       
+        //CODE HERE BELOW IS EXECUTED ONLY IF THE MARKER HAS CAN MOVE IMPLEMENTED.
+        //Otherwise, no mouse event can call these methods
+        //re-implement function move
+        var position = 0;
+        var relativePosition = 0; //position in percentage of container width, set it in move and use it in refreshPosition
+        
+        var mRound = Math.round; //instantiate the functio once
+
+        this.move = function(pixelOffset) {
+            var width =  viewer.width();
+            if (position != pixelOffset) {
                 if (pixelOffset < 0) {
                     pixelOffset = 0;
-                } else if (pixelOffset >= this.width) {
-                    pixelOffset = this.width - 1;
+                } else if (pixelOffset >= width) {
+                    pixelOffset = width - 1;
                 }
-                this.nodes.each(function(i, node) {
-                    $J(node).css('left', Math.round(node.originalPosition + pixelOffset) + 'px');
+                nodes.each(function(i, node) {
+                    $J(node).css('left', mRound(node.originalPosition + pixelOffset) + 'px');
                 });
-                var labelWidth = this.label.width();
-                var labelPixelOffset = pixelOffset - labelWidth / 2;
-                if (labelPixelOffset < 0)
-                    labelPixelOffset = 0;
-                else if (labelPixelOffset + labelWidth > this.width)
-                    labelPixelOffset = this.width - labelWidth;
-                this.label.css({
-                    left: Math.round(labelPixelOffset) + 'px'
-                });
-                this.position = pixelOffset;
+                position = pixelOffset;
+                this.refreshLabelPosition(width);
+                //store relative position (see refreshPosition below)
+                relativePosition = pixelOffset == width-1 ? 1 : pixelOffset/width;
             }
             return this;
-        },
+        };
 
-        show: function(offset) {
-            if (!this.visible) {
-                this.nodes.show();
-                this.label.show();
-                this.visible = true;
+        this.refreshLabelPosition = function(optionalContainerWidth){
+            if(!(optionalContainerWidth)){
+                optionalContainerWidth = viewer.width();
             }
-            return this;
-        },
-
-        hide: function() {
-            this.nodes.hide();
-            this.label.hide();
-            this.visible = false;
-            return this;
-        },
-
-        isVisible: function() {
-            return this.visible;
-        },
-
-        blink: function(state) {
-            var speed = 200;
-            if (this.label && this.blinking != state) {
-                var span = this.label.find('span');
-
-                span.stop();
-
-                function fade(on) {
-                    if (on) {
-                        span.animate({
-                            opacity: 1
-                        }, speed, null,
-                        function() {
-                            fade(false)
-                        });
-                    } else {
-                        span.animate({
-                            opacity: 0.4
-                        }, speed, null,
-                        function() {
-                            fade(true)
-                        })
-                    }
-                }
-
-                if (state) {
-                    fade();
-                } else {
-                    span.animate({
-                        opacity: 1
-                    }, speed);
-                }
-
-                this.blinking = state;
+            var width = optionalContainerWidth;
+            var pixelOffset = position;
+            var labelWidth = label.outerWidth(); //consider margins and padding //label.width();
+            var labelPixelOffset = pixelOffset - labelWidth / 2;
+            if (labelPixelOffset < 0){
+                labelPixelOffset = 0;
+            }else if (labelPixelOffset + labelWidth > width){
+                labelPixelOffset = width - labelWidth;
             }
-            return this;
-        },
+            label.css({
+                left: mRound(labelPixelOffset) + 'px'
+            });
 
-        _onMouseDown: function(evt) {
-            this.mouseDown = true;
-            this.mouseDownOffset = evt.pageX-(this.label.offset().left+this.label.outerWidth(true)/2);
-            //this._onMouseMove(evt);
-            return false;
-        },
+        };
+
+        //function called on ruler.resize. Instead of recreating all markers, simply redraw them
+        this.refreshPosition = function(){
+            var width =  viewer.width();
+            //store relativePosition:
+            var rp = relativePosition;
+            this.move(mRound(relativePosition*width));
+            //reset relative position, which does not have to change
+            //but in move might have been rounded:
+            relativePosition = rp;
+            //last thing: resize the vertical line.
+            //Assumptions (having a look at the web page element with a debugger and the code above
+            //which uses jsgraphics):
+            //The line is the first item (see drawLine above)
+            //not only the height, but also the height of the clip property must be set
+            var h = viewer.height();
+            $J(nodes[0]).css({
+                'height':h+'px',
+                'clip': 'rect(0px 1px '+h+'px 0px)'
+            });
+        }
 
-        _onMouseMove: function(evt) {
-            if (this.mouseDown) {
-                var offset = (evt.pageX - this.cfg.rulerLayout.offset().left)-this.mouseDownOffset;
-                this.debug(evt.pageX);
-                this.debug(this.label.outerWidth(true));
-                this.debug(this.cfg.rulerLayout.offset().left);
-                this.move(offset);
-                this.fire('move', { //calls move (see above)
-                    offset: this.position,
-                    finish: false
+        this.remove = function() {
+            painter.clear();
+            $J(painter.cnv).remove();
+            label.remove();
+            return this;
+        };
+    },
+
+    //sets the text of the marker, if the text changes the marker width and optionalUpdateLabelPosition=true,
+    //re-arranges the marker position to be center-aligned with its vertical line (the one lying on the wav image)
+    setText: function(text, optionalUpdateLabelPosition) {
+        var label = this.getLabel();
+        if (label) {
+            text += '';
+            var labelWidth = this._textWidth(text, this.getFontSize()) + 10;
+            var oldWidth = label.width();
+            if (oldWidth != labelWidth) {
+                label.css({
+                    width: labelWidth+'px'
                 });
-                return false;
             }
-        },
-
-        _onMouseUp: function(evt) {
-            if (this.mouseDown) {
-                this.mouseDown = false;
-                this.fire('move', {
-                    index: this.index,
-                    offset: this.position,
-                    finish: true
-                });
-                return false;
+            label.find('span').html(text);
+            if(oldWidth != labelWidth && optionalUpdateLabelPosition){
+                consolelog('refreshing label position');
+                this.refreshLabelPosition();
             }
-        },
-
-        _observeMouseEvents: function() {
-            this.label.mousedown(this.attachWithEvent(this._onMouseDown))
-            .bind('click dragstart', function() {
-                return false;
-            });
-            this.cfg.rulerLayout.mousemove(this.attachWithEvent(this._onMouseMove));
-            this.cfg.rulerLayout.mouseup(this.attachWithEvent(this._onMouseUp));
-            $J(document).mouseup(this.attachWithEvent(this._onMouseUp));
         }
-
-    //    _toString: function() {
-    //        return "<marker id="+id+" position="+position+" description=\""+
-    //            +description+"\"/>";
-    //    }
-
-
-    });
-
-    $N.notifyScriptLoad();
+        return this;
+    }
 
 });
index 19055133c7a032c666556488a5026a5c1cbc2e94..30f33f880afe4120d6ebff3b37174b525d3be3ac 100644 (file)
 /**
  * TimeSide - Web Audio Components
- * Copyright (c) 2008-2009 Samalyse
- * Author: Olivier Guilyardi <olivier samalyse com>
+ * Author: Riccardo Zaccarelli and Olivier Guilyardi <olivier samalyse com>
  * License: GNU General Public License version 2.0
  */
 
-//this global variable SEEMS to do a check on the variable jQuery, then
-//simply executes the argument (which is a function)
-var TimeSide = function() {
-    //arguments is an array-like object corresponding to the arguments passed to a function
-    if (arguments[0]) {
-        var toolkit = null;
-        if (typeof jQuery != 'undefined'){
-            toolkit = jQuery;
-        }
-        //call arguments[0] (a function) with arguments this and jQuery
-        (arguments[0])(TimeSide, toolkit)
-    }
-};
-//this is the first function instantiated. It SEEMS to check the document status and
-//load synchronously all the scripts
-TimeSide(function($N, $J) {
-
-    $N.isDomLoaded = false;
-    $N.isLoaded = false;
-    $N.isLoading = false;
-    $N.onLoadCallbacks = [];
-    $N.cssPrefix = 'ts-';
-    $N.debugging = true;
-
-    $J(document).ready(function () {
-        $N.isDomLoaded = true;
-    });
+/* Simple JavaScript Inheritance
+ * By John Resig http://ejohn.org/
+ * MIT Licensed.
+ * (Inspired by base2 and Prototype)
+ * 
+ * In my opinion the lightest (no tons of heavy .js files to be included) and
+ * easiest (no days spent understanding what is going on) way to implement inhertance and OOP in
+ * javascript. Usages can be found below.
+ * Basically,
+ * 1) a new Class is instantiated with Class.extend(). This function takes a dictionary
+ * of properties/methods which will be put IN THE PROTOTYPE of the class, so that each instance will share the same properties/methods
+ * and the latter don't have to be created for each instance separately.
+ * 2) If var A = Class.extend({...}) and var B = A.extend({..}), then methods which are found in B will override the same methods in A.
+ * In this case, the variable this._super inside the overridden methods will refers to the super-method and can thus be called safely.
+ * Consequently, if a _super property/method is implemented in the extend dictionary, it WILL NOT be accessible
+ * to the overriding methods of B. Basically, don't use _super as a key of the argument of extend.
+ * 3) AFTER the prototype has been populated, the init function, if exists, is called. The latter can be seen as a class constructor in java.
+ * Private variable can be declared in the init function (which is used as class constructor), as well as
+ * relative getters and setters, if needed. Downside is that the privileged getters and setters can’t be put in the prototype,
+ * i.e. they are created for each instance separately. Another issue is the overhead of closures in general (basically, write as less as possible
+ * in the init functionm in particular if the class has to be declared several times)
+ * Of course, the this._super keyword of methods implemented in the init constructor does not work
+ *
+ * EXAMPLE:
+ * var MyClass = Class.extend({
+ *   init: function(optionalArray){ //constructor
+ *       this._super();             //!!!ERROR: Class is the base class and does not have a super construcor
+ *       var me = [];               //private variable
+ *       this.count = 6;            //set the value of the public property defined below
+ *       this.getMe = function(){   //public method
+ *           this._super();         //!!!ERROR: methods defined in the init function don't have acces to _super
+ *       }
+ *       this.alert = function(){   //another public method, !!!WARNING: this will be put in the MyClass scope (NOT in the prototype)
+ *           alert('ok');           
+ *       }
+ *   },
+ *   count:0,                       //public property
+ *   alert: function(){             //public method. !!!WARNING: this method will be put in the prototype BEFORE the init is called,
+ *      alert('no');                //  so the alert defined above will be actually called
+ *   }
+ * });
+ * var MyClass2 = MyClass.extend({
+ *  init: function(){
+ *      this._super();                  //call the super constructor
+ *  }
+ *  alert: function(){                  //override a method
+ *      this._super();                  //call the super method, ie alerts 'no'. WARNING: However, as long as there is an alert written 
+ *                                      //in the init method of the superclass (see above), THAT method will be called
+ *  }
+ * });
+ *
+ */
+// 
+(function(){
+
+    var initializing = false, fnTest = /xyz/.test(function(){
+        xyz;
+    }) ? /\b_super\b/ : /.*/;
+
+    /*The xyz test above determines whether the browser can inspect the textual body of a function.
+     *If it can, you can perform an optimization by only wrapping an overridden method if it
+     *actually calls this._super() somewhere in its body.
+     *Since it requires an additional closure and function call overhead to support _super,
+     *it’s nice to skip that step if it isn’t needed.
+     */
+
+    // The base Class implementation (does nothing)
+    this.Class = function(){};
+
+    // Create a new Class that inherits from this class
+    Class.extend = function(prop) {
+        var _super = this.prototype;
+
+        // Instantiate a base class (but only create the instance,
+        // don't run the init constructor)
+        initializing = true;
+        var prototype = new this();
+        initializing = false;
+
+        // Copy the properties over onto the new prototype
+        for (var name in prop) {
+            // Check if we're overwriting an existing function
+            prototype[name] = typeof prop[name] == "function" &&
+            typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+            (function(name, fn){
+                return function() {
+                    var tmp = this._super;
 
-    $N.domReady = function(callback) {
-        // simply calling jQuery.ready() *after* the DOM is loaded doesn't work reliably,
-        // at least with jQuery 1.2.6
-        if ($N.isDomLoaded) {
-            callback();
-        } else{
-            $J(document).ready(callback);
+                    // Add a new ._super() method that is the same method
+                    // but on the super-class
+                    this._super = _super[name];
+
+                    // The method only need to be bound temporarily, so we
+                    // remove it when we're done executing
+                    var ret = fn.apply(this, arguments);
+                    this._super = tmp;
+
+                    return ret;
+                };
+            })(name, prop[name]) :
+            prop[name];
         }
-    }
 
-    $N.instances = [];
-    $N.registerInstance = function(obj) {
-        $N.instances.push(obj);
-    }
+        // The dummy class constructor
+        function Class() {
+            // All construction is actually done in the init method
+            if ( !initializing && this.init ){
+                this.init.apply(this, arguments);
+            }
+        }
 
-    $N.free = function() {
-        $J($N.instances).each(function(i, obj) {
-            obj.free();
-        });
-    }
+        // Populate our constructed prototype object
+        Class.prototype = prototype;
+        
+        // Enforce the constructor to be what we expect
+        Class.constructor = Class;
+
+        // And make this class extendable
+        Class.extend = arguments.callee;
 
-    $J(window).unload($N.free);
+        return Class;
+    };
+})();
 
-    $N.loadScriptsNum = 0;
-    $N.loadScriptsCallback = null;
-    $N.loadScripts = function(root, scripts, callback) {
-        if ($N.loadScriptsCallback) {
-            throw "Timeside loader error: concurrent script loading";
+//Defining the base TimeClass class. Player, Ruler, MarkerMap are typical implementations (see js files)
+//Basically we store here static methods which must be accessible
+//in several timside sub-classes
+var TimesideClass = Class.extend({
+    _textWidth : function(text, fontSize) {
+        var ratio = 3/5;
+        return text.length * ratio * fontSize;
+    },
+    
+    //formats (ie returns a string representation of) a time which is in the form seconds,milliseconds, eg 07.6750067
+    //formatArray is an array of strings which can be:
+    // 'h' hours. Use 'hh' for a zero-padding to 10 (so that 6 hours is rendered as '06')
+    // 'm' hours. Use 'mm' for a zero-padding to 10 (so that 6 minutes is rendered as '06')
+    // 's' hours. Use 'ss' foar a zero-padding to 10 (so that 6 seconds is rendered as '06')
+    // 'D' deciseconds
+    // 'C' centiseconds (it will be padded to 10, so that 5 centiseconds will be rendered as '05')
+    // 'S' milliseconds (it will be padded to 100, so that 5 milliseconds will be rendered as '005')
+    // If formatArray is null or undefined or zero-length, it defaults to ['mm','ss']
+    // 'h','m' and 's' will be prepended the separator ':'. For the others, the prepended separator is '.'
+    // Examples:
+    // makeTimeLabel(607,087) returns '10:07'
+    // makeTimeLabel(611,087,['m':'s']) returns '10:7'
+    // makeTimeLabel(611,087,['m':'s','C']) returns '10:7.09'
+    //========================================================================================
+    makeTimeLabel: function(time, formatArray){
+        if(!(formatArray)){
+            formatArray = ['mm','ss'];
         }
+        //marker offset is in float format second.decimalPart
+        var pInt = parseInt;
+        var round = Math.round;
+        var factor = 60*24;
+        var hours = pInt(time/factor);
+        time-=hours*factor;
+        factor = 60;
+        var minutes = pInt(time/factor);
+        time-=minutes*factor;
+        var seconds = pInt(time);
+        time-=seconds;
 
-        $N.loadScriptsNum = scripts.length;
-        $N.loadScriptsCallback = callback;
+        //here below the function to format a number
+        //ceilAsPowerOfTen is the ceil specifiedas integer indicating the relative power of ten
+        //(0: return the number as it is, 1: format as "0#" and so on)
+        //Examples: format(6) = "6", format(6,1)= "06", format(23,1)= "23"
 
-        var head= document.getElementsByTagName('head')[0];
-        for (i = 0; i < scripts.length; i++) {
+        //first of all, instantiate the power function once (and not inside the function or function's loop):
+        var mpow = Math.pow; //instantiate mpow once
+        var format = function(integer,ceilAsPowerOfTen){
+            var n = ""+integer;
+            if(!(ceilAsPowerOfTen)){
+                return n;
+            }
+            var zero = "0"; //instantiating once increases performances???
+            for(var i=0; i< ceilAsPowerOfTen; i++){
+                if(integer<mpow(10,i+1)){
+                    n = zero+n;
+                }
+            }
+            return n;
+        }
+        var ret = [];
+        for(var i =0; i<formatArray.length; i++){
+            var f = formatArray[i];
+            var separator = ":";
+            if(f=='h'){
+                ret[i]=hours;
+            }else if(f=='hh'){
+                ret[i]=format(hours,1);
+            }else if(f=='m'){
+                ret[i]=minutes;
+            }else if(f=='mm'){
+                ret[i]=format(minutes,1);
+            }else if(f=='s'){
+                ret[i]=seconds;
+            }else if(f=='ss'){
+                ret[i]=format(seconds,1);
+            }else if(f=='S'){
+                separator = ".";
+                ret[i]=format(round(time*1000),3);
+            }else if(f=='C'){
+                separator = ".";
+                ret[i]=format(round(time*100),2);
+            }else if(f=='D'){
+                separator = ".";
+                ret[i]=format(round(time*10),1);
+            }
+            if(i>0){
+                ret[i] = separator+ret[i];
+            }
+        }
+        return ret.join("");
+    },
 
-            var script = document.createElement('script');
-            script.type = 'text/javascript';
-            var debug = $N.debugging ? '?rand=' + Math.random() : '';
-            script.src = root + scripts[i] + debug;
-            head.appendChild(script);
+    cssPrefix : 'ts-',
+    $J : jQuery,
+    debugging : true,
+    debug : function(message) {
+        if (this.debugging && typeof console != 'undefined' && console.log) {
+            console.log(message);
+        //console.log('TimeSide.' + this.__class__.__name__ + ': ' + message);
         }
-    }
+    },
+    //init constructor. Define the 'bind' and 'fire' (TODO: rename as 'trigger'?) methods
+    //we do it in the init function so that we can set a private variable storing all
+    //listeners. This means we have to re-write all methods
+    init: function(){
+       
+        //the map for listeners. Must be declared in the init as it's private and NOT shared by all instances
+        //(ie, every instance has its own copy)
+        var listenersMap={};
+        //follows jquery bind. Same as adding a listener for a key
+        this.bind = function(key, callback, optionalThisArgInCallback){
+            if(!(callback && callback instanceof Function)){
+                this.debug('cannot bind '+key+' to callback: the latter is null or not a function');
+                return;
+            }
+            var keyAlreadyRegistered = (key in listenersMap);
+            if(!keyAlreadyRegistered){
+                listenersMap[key] = [];
+            }
+            listenersMap[key].push({
+                callback:callback,
+                optionalThisArgInCallback:optionalThisArgInCallback
+            });
+        };
+        this.unbind = function(){
+            if(arguments.length>0){
+                var key = arguments[0];
+                if(key in listenersMap){
+                    delete listenersMap[key];
+                }
+            }else{
+                listenersMap={};
+            }
+        };
+        this.fire = function(key, dataArgument){
+            if(!(key in listenersMap)){
+                this.debug(key+' fired but no binding associated to it');
+                return;
+            }
+            var callbacks = listenersMap[key];
+            var len = callbacks && callbacks.length ? callbacks.length : 0;
+            for(var i=0; i<len; i++){
+                var obj = callbacks[i];
+                if('optionalThisArgInCallback' in obj){
+                    obj.callback.apply(obj.optionalThisArgInCallback, [dataArgument]);
+                }else{
+                    obj.callback(dataArgument);
+                }
+            }
+        };
+    },
+
+    /* creates all elements in skeleton and appends them in container. Returns
+        * the jQuery object representing all elements created
+        *
+        * skeleton can be is a javascript object or array. 
+        * 1) a javascript array. In this case its elements must be strings according to the syntax 'tag.className' or simply 'tag', 
+        * eg, 'div.classname', 'a' etcetera
+        * 2) a javascript object. In this case its keys must be strings in the same syntax as specified above
+        * and the values associated to each key must be in turn an object or an array following again the same syntax 
+        * in order to represent the subelements of key to be created
+        * Example1
+        *   skeleton = { 'div.className':['a.className2'] };
+        *   UI(container, skeleton);
+        * will return a jQuery object of the newly created div (with class 'className') which has been appended to container
+        * the returned div has an anchor element as child with class 'className2'
+        * Example2
+        *   skeleton= {'div.className':{}, ', 'div':{}};
+        *   UI(container, skeleton)
+        * will return a jQuery object of the 2 newly created divs (the former with class 'className')
+        * which have been appended to container
+        */
+    loadUI: function(container, skeleton) {
+        var i = 0;
+        var $J = this.$J;
+        var elements = $J([]); //create empty jquery object
+        
+        
+        var _loadChild = function(container_, tag, className, index) {
+            var element = container_.find('.'+className);
+            if (!element.length) {
+                element = $J(document.createElement(tag)).addClass(className);
+                var children = container_.children();
+                //consolelog('loadUI: inserting ' + element.attr('class')+' in '+container_.attr('class'));
+                if (index < children.length) {
+                    children.eq(index).before(element);
+                } else {
+                    container_.append(element);
+                }
+            }else{
+            //consolelog('loadUI: returning ' + element.attr('class')+' (already present) ');
+            }
+            return element;
+        };
+
+        if (skeleton[0]) {
+            $J(skeleton).each((function(i, selector) {
+                var s = selector.split('.');
+                var newChild = _loadChild(container, s[0], s[1], i++);
+                elements = elements.add(newChild);
+            }));
+        } else {
+            for (var key in skeleton) {
+                var s = key.split('.');
+                var subcontainer = _loadChild(container, s[0], s[1], i++);
+                elements = elements.add(subcontainer);
+                elements = elements.add(this.loadUI(subcontainer, skeleton[key]));
+                
+            //elements[$N.Util.camelize(s[1])] = e;
+            //$N.extend(elements, UI(e, skeleton[key], contents));
 
-    $N.notifyScriptLoad = function() {
-        if (--$N.loadScriptsNum == 0 && $N.loadScriptsCallback) {
-            var callback = $N.loadScriptsCallback;
-            $N.loadScriptsCallback = null;
-            callback();
+            }
         }
+       
+        return elements;
     }
 
-    $N.debug = function(state) {
-        $N.debugging = state;
-    }
+});
 
-    $N.load = function(callback) {
-        $N.domReady(function() {
-            if ($N.isLoaded) {
-                if (callback){
-                    callback();
-                }
-            } else {
-                if (callback)
-                    $N.onLoadCallbacks.push(callback);
-
-                if (!$N.isLoading) {
-                    $N.isLoading = true;
-                    var re = /(.*)timeside.js/;
-                    var root = '/timeside/src/'; //TODO: changed by me!!!!!
-                    $J('head script').each(function(i, e) {
-                        if ((match = re.exec(e.src))) {
-                            root = match[1];
-                        }
-                    });
-
-                    $N.loadScripts(root, ['core.js'], function() {
-                        $N.loadScripts(root, ['util.js'], function() {
-                            var scripts = ['controller.js', 'rulermarker.js', //'markerlist.js',
-                            'markermap.js', 'player.js', 'ruler.js','divmarker.js'];
-                            //,'soundprovider.js'];
-                                                       
-                            $N.loadScripts(root, scripts, function() {
-                                $N.isLoaded = true;
-                                $N.isLoading = false;
-                                $J($N.onLoadCallbacks).each(function(i, callback) {
-                                    callback();
-                                    //console.log(callback.toString());
-                                });
-                            });
-                        });
-                    });
+//re-implemented array for easier access/modification of markers:
+//Ruler, MArkerMap and MarkerMapDiv implement this class
+var TimesideArray = TimesideClass.extend({
+    init: function(optionalArray){
+        this._super();
+        //here methods that CANNOT be overridden
+        var me= optionalArray ? optionalArray : [];
+        //note that this method written here OVERRIDES the same method written outside init in the children!!!!
+        this.toArray = function(returnACopy){
+            if(returnACopy){
+                consolelog('copying array');
+                var ret = [];
+                for(var i=0; i<me.length; i++){
+                    ret.push(me[i]);
                 }
+                return ret;
             }
+            return me;
+        }
+        this.length = me.length; //in order to match the javascript array property
+    },
+    length:0, //implement it as public property to be consistent with Array length property. However, DO NOT modify directly this property!!!
+    //adds at the end of the array. If index is missing the object is appended at the end
+    add : function(object, index){
+        var array = this.toArray();
+        if(arguments.length<2){
+            index = array.length;
+        }
+        array.splice(index,0,object);
+        this.length = array.length; //note that length is a property and must be updated!!!
+        return object;
+    },
+    //removes item at index, returns the removed element
+    remove : function(index){
+        var array = this.toArray();
+        var ret =  array.splice(index,1)[0];
+        this.length = array.length; //note that length is a property and must be updated!!!
+        return ret;
+    },
+    //Iterate over the array, with the same syntax of jQuery.each, ie, executes a function(index,element)
+    //for each element from startIndexInclusive to
+    //endIndexExclusive.
+    //The only required argument is callback:
+    //1) each(callback) iterates over all elements executing callback
+    //2) each(m, callback) iterates over the elements from m executing callback
+    //3) each(m,n,callback) iterates over the elements from m (inclusive) to n-1 (inclusive) executing callback
+
+    //NOTE: writing   each : function(startInclusive, endExclusive, callback) throws an error in chrome, as the last 
+    //argument (even if it is a function) is a number. Why?????
+    //Anyway, we must write the function arguments as empty
+    each : function(){
+        //        consolelog(arguments.length+' arguments passed. Details: ');
+        //        for(var j=0; j<arguments.length; j++){
+        //            consolelog('arguments['+ j+']: ');consolelog(arguments[j]);
+        //        }
+        var startInclusive, endExclusive, callback;
+
+        var arg = arguments;
+        var len = arg.length;
+        var l = this.length;
+        switch(len){
+            case 0:
+                this.debug('each called without arguments!!!');
+                return;
+            case 1:
+                //callback = arg[0];
+                startInclusive = 0;
+                endExclusive = l;
+                break;
+            case 2:
+                if(arg[0] >= l){
+                    return;
+                }
+                startInclusive = arg[0]=== undefined ? 0 : arg[0];
+                endExclusive = l;
+                //callback = arg[len-1];
+                break;
+            default:
+                startInclusive = arg[0]=== undefined ? 0 : arg[0];
+                endExclusive = arg[1]=== undefined ? l : arg[1];
+        //callback = arg[len-1];
+        }
+        callback = arg[len-1];
+        if(!(callback instanceof Function)){
+            this.debug('callback NOT a function!!!');
+            return;
+        }
+        var me =this.toArray();
+        for(var i = startInclusive; i<endExclusive; i++){
+            callback(i,me[i]);
+        }
+    //
+    //
+    //        if(!(len) || arg[len-1]===undefined){
+    //            consolelog('each called without arguments!!!');
+    //            return;
+    //        }
+    //        callback = arg[len-1];
+    //        //return immediately if startinedx exists and is greater or equal this.length
+    //        if(len>1 && arg[0] >= this.length){
+    //            return;
+    //        }
+    //        if(len>1 && startInclusive=== undefined){
+    //            consolelog('oops---------------- startindex');
+    //        }else if(len>2 && endExclusive=== undefined){
+    //            consolelog('oops---------------- endIndex');
+    //        }
+    //
+    //        var formatvar = function(array,index, correctionValue){
+    //            if(array.length>index && array[index]!== undefined){
+    //                return array[index];
+    //            }
+    //            return correctionValue;
+    //        }
+    //        var me =this.toArray();
+    //        var s = len > 1 ? formatvar(arg,0,0) : 0;
+    //        var e = len > 2 ? formatvar(arg,1,me.length) : me.length;
+    //        //consolelog(s+' - '+e);
+    //        for(var i = s; i<e; i++){
+    //            callback(i,me[i]);
+    //        }
+    },
+
+    //clears the array and the events associated to it, ie removes all its elements and calls unbind(). Returns the array of the removed elements
+    clear: function(){
+        this.unbind();
+        var me = this.toArray();
+        var l = me.length;
+        this.length = 0;
+        if(l==0){
+            return [];
+        }
+        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
+    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 defaullts 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: that the last 2 arguments are intended for css "visual look", as the relevant css will be overridden inside the code.
+ * With relevant css we mean all css necessary to let tabs behave like a 'desktop application tab' (eg, (position, top, zIndex). Note also
+ * that very tab parent container' of every tab's visibility is set to 'visible' (the default)
+ *
+ * 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]);
+            // consolelog(t.attr('id')+' is '+(i==index ? 'showing ' : 'hiding ')+div.attr('id'));
+            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");
+}
\ No newline at end of file
index c05199f01481c0da0de8609126c06b016e4bd36e..727d782398ff8e221c12cb3c8b719acb6829c97c 100644 (file)
 <script type="application/x-javascript" src="{% url jsonrpc_browser %}?f=interpreter.js"></script>-->
 <script src="{% url telemeta-js "jquery.js" %}" type="text/javascript"></script>
 <script src="{% url telemeta-js "application.js" %}" type="text/javascript"></script>
+<script type="text/javascript" src="{% url telemeta-js-translations 'telemeta'  %}"></script>
+<script type="text/javascript">
+    //alert(gettext('Change password'));
+</script>
 <!--<script src="{% url telemeta-js "json.js" %}" type="text/javascript"></script>-->
 
 {% block extra_javascript %}{% endblock %}
index 2cba3efceb0a20de95a16a5dbe8627da97d99732..12ddabdfa16b561556b85146274f7c9af21d2c93 100644 (file)
             <!-- this div will be hidden when everything is fully loaded-->
             <span id="loading_span" href="#"><img style="vertical-align:middle" src="/images/wait.gif"/>&nbsp;
                 <span id="loading_span_text">Loading...</span></span>
-            <a id="tab_analysis" style="display:none" name ="analyzer_div_id" href="#">{% trans "Analysis" %}</a>
-            <a id="tab_markers" style="display:none" name="markers_div_id" href="#">{% trans "Markers" %}</a>
+            <a id="tab_analysis" style="display:none" class ="tab" href="#">{% trans "Analysis" %}</a><!--
+             do not let space here as it appears in the document
+            --><a id="tab_markers" style="display:none" class="tab" href="#">{% trans "Markers" %}</a>
         </div>
 
         <div class="markers" id="markers_div_id"></div>
index 7a297449504d4eae735d8984bc992c40bc50e609..a98b44dd0a10239d46587707e497ee53b5cb0f14 100644 (file)
@@ -59,6 +59,7 @@ export_extensions = "|".join(web_view.list_export_extensions())
 
 htdocs = os.path.dirname(__file__) + '/htdocs'
 
+
 urlpatterns = patterns('',
     url(r'^$', web_view.index, name="telemeta-home"),
     url(r'^help$', web_view.help, name="telemeta-help"),
@@ -253,5 +254,11 @@ urlpatterns = patterns('',
     
     # Not allowed
     url(r'/*/(?P<public_id>[A-Za-z0-9._-]+)/not_allowed/$', web_view.not_allowed, name="telemeta-not-allowed"),
-    
+
+    #i18n javascript
+    url(r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog', name="telemeta-js-translations")
+
+
 )
+
+