}
-.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;
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;
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;
}
/*----------------------------------*/
//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
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
}\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
//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
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
\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
}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
-//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');
//form.append(img);
setTimeout(function(){
- change_visualizer();
+ if (player){
+ player.refreshImage();
+ }
//img.remove();
setTimeout(function(){
if(src){
},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){
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>");
//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;
}
});
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);
}
-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);
- }
-}
/**
* 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
/**
* 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
-/**
- * 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
-/**
- * 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(' ');
- 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;
} 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
-/**
- * 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;
+ }
});
/**
* 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
<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 %}
<!-- this div will be hidden when everything is fully loaded-->
<span id="loading_span" href="#"><img style="vertical-align:middle" src="/images/wait.gif"/>
<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>
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"),
# 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")
+
+
)
+
+