From: yomguy Date: Mon, 16 May 2011 17:04:08 +0000 (+0200) Subject: merge from unstable r847 X-Git-Tag: 1.3.9~3^2~1^2~118 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=0340912475197bd5481846d544ad110773b3498a;p=telemeta.git merge from unstable r847 --- 0340912475197bd5481846d544ad110773b3498a diff --cc telemeta/htdocs/timeside/js/controller.js index 00000000,00000000..0683a511 new file mode 100644 --- /dev/null +++ b/telemeta/htdocs/timeside/js/controller.js @@@ -1,0 -1,0 +1,164 @@@ ++/** ++ * TimeSide - Web Audio Components ++ * Copyright (c) 2008-2009 Samalyse ++ * Author: Olivier Guilyardi ++ * License: GNU General Public License version 2.0 ++ */ ++ ++TimeSide(function($N) { ++ ++ $N.Class.create("Controller", $N.Core, { ++ ++ initialize: function($super, cfg) { ++ $super(); ++ this.configure(cfg, { ++ player: null, ++ soundProvider: null, ++ map: null, ++ divmarkers:[] ++ }); ++ if (this.cfg.player && !$N.isInstanceOf(this.cfg.player, 'Player')) { ++ this.cfg.player = new $N.Player(this.cfg.player); ++ } ++ this._setupPlayer(); ++ this.loadHTTP(); ++ ++ }, ++ ++ _setupPlayer: function() { ++ this.debug('_setupPlayer'); ++ this.cfg.player ++ .setMarkerMap(this.cfg.map) ++ .observe('markeradd', this.attach(this._onMarkerAdd)) ++ //player markermove listens for changes of ruler markermove which listens ++ //foir changes in each marker move ++ .observe('markermove', this.attach(this._onMarkerMove)) ++ ._setupInterface(); ++ ++ this.cfg.map.observe('add',this.attach(this._onMarkerMapAdd)); ++ this.cfg.map.observe('remove',this.attach(this._onMarkerMapRemove)); ++ this.cfg.map.observe('moved',this.attach(this._onMarkerMapMoved)); ++ ++ }, ++ ++ //called whenever a marker is moved in the ruler BUT NOT in the map ++ _onMarkerMove: function(e, data) { ++ if (this.cfg.map) { ++ $N.Util.selectMarkerTab(); //defined in utils.js ++ this.cfg.map.move(data.index, data.offset); ++ //this will fire the method below ++ } ++ }, ++ ++ _onMarkerMapMoved:function(e, data){ ++ var from = data.fromIndex; ++ var to = data.toIndex; ++ this.cfg.divmarkers.move(from,to); //new array method see application.js ++ this.cfg.player.ruler.markers.move(from,to); ++ this.updateIndices(from,data.newIndex); ++ this.divFocus(data.newIndex); ++ }, ++ ++ divFocus: function(divIndex){ ++ if(this.cfg.divmarkers){ ++ var max = this.cfg.divmarkers.length; ++ for (var i = 0; i < max; i++) { ++ if(i==divIndex){ ++ this.cfg.divmarkers[i].focusOn(); ++ }else{ ++ this.cfg.divmarkers[i].focusOff(); ++ } ++ } ++ } ++ }, ++ //called whenever a marker is added to the ruler BUT NOT in the map ++ _onMarkerAdd: function(e, data) { ++ if (this.cfg.map) { ++ $N.Util.selectMarkerTab(); //defined in mediaitem|_detail.html ++ var idx = this.cfg.map.add(data.offset); //this will call the method below _onMarkerMapAdd, ++ //which btw adds a new div to divmarkers ++ //now update the indices for the div (which also sets the event bindings as clicks etc... ++ this.updateIndices(idx); ++ this.divFocus(idx); ++ } ++ }, ++ //fired from markermap, attached as listener above in ++ //this.cfg.map.observe('add',this.attach(this._onMarkerMapAdd)); ++ //this method basically adds the html elements, but updateIndices must be called elsewhere after this function ++ //(see _onMarkerAdd and loadHTTP) ++ _onMarkerMapAdd: function(e, data) { ++ if (this.cfg.map) { ++ ++ var idx = data.index; ++ this.cfg.divmarkers.splice(idx,0, new $N.DivMarker(this.cfg.map)); ++ this.cfg.player.ruler.onMapAdd(data.marker, idx); ++ } ++ }, ++ ++ //fired from markermap, attached as listener above in ++ //this.cfg.map.observe('add',this.attach(this._onMarkerMapAdd)); ++ _onMarkerMapRemove: function(e, data) { ++ if (this.cfg.map) { ++ var idx = data.index; ++ var divRemoved = this.cfg.divmarkers.splice(idx,1)[0]; //there is only one element removed ++ divRemoved.remove(); ++ this.cfg.player.ruler.remove(idx); ++ this.updateIndices(idx); ++ ++ } ++ }, ++ ++ updateIndices: function(from, to){ ++ if(from===undefined || from==null){ ++ from = 0; ++ } ++ var len = this.cfg.divmarkers.length-1; ++ if(from>len){ ++ return; ++ } ++ if(to==undefined || to ==null){ ++ to = len; ++ } ++ if(to0){ ++ var result = data.result; ++ ++ for(var i =0; i< result.length; i++){ ++ map.add(result[i]); ++ } ++ //we call now updateindices ++ updateIndices.apply(me); ++ tabIndex = result.length>0 ? 1 : 0; ++ } ++ ++ } ++ util.setUpTabs.apply(util, [tabIndex]); ++ }; ++ json([itemId],"telemeta.get_markers", onSuccess); ++ } ++ }); ++ ++ $N.notifyScriptLoad(); ++ ++}); diff --cc telemeta/htdocs/timeside/js/core.js index 00000000,00000000..e86b6917 new file mode 100644 --- /dev/null +++ b/telemeta/htdocs/timeside/js/core.js @@@ -1,0 -1,0 +1,293 @@@ ++/** ++ * TimeSide - Web Audio Components ++ * Copyright (c) 2008-2009 Samalyse ++ * Author: Olivier Guilyardi ++ * License: GNU General Public License version 2.0 ++ */ ++ ++TimeSide(function($N, $J) { ++ ++ $N.extend = function(destination, source) { ++ for (var property in source){ ++ destination[property] = source[property]; ++ } ++ return destination; ++ }; ++ ++ $N.objectKeys = function(object) { ++ var keys = []; ++ for (var property in object){ ++ keys.push(property); ++ } ++ return keys; ++ }; ++ ++ $N.argumentNames = function(method) { ++ var names = method.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1] ++ .replace(/\s+/g, '').split(','); ++ return names.length == 1 && !names[0] ? [] : names; ++ }; ++ ++ $N.argsToArray = function(args) { ++ var length = args.length || 0, result = new Array(length); ++ while (length--){ ++ result[length] = args[length]; ++ } ++ return result; ++ }; ++ ++ $N.wrapFunction = function(wrapper, method) { ++ return function() { ++ var args = $N.argsToArray(arguments); ++ return wrapper.apply(this, [$N.attachFunction(this, method)].concat(args)); ++ } ++ }; ++ ++ $N.attachFunction = function() { ++ if (arguments.length < 3 && (typeof arguments[1] == 'undefined')){ ++ return arguments[0]; ++ } ++ var args = $N.argsToArray(arguments); ++ var object = args.shift(); ++ var method = args.shift(); ++ return function() { ++ var _args = $N.argsToArray(arguments); ++ return method.apply(object, args.concat(_args)); ++ } ++ }; ++ ++ $N.attachAsEventListener = function() { ++ var args = $N.argsToArray(arguments), object = args.shift(); ++ var method = args.shift(); ++ return function(event) { ++ return method.apply(object, [event || window.event].concat(args)); ++ } ++ }; ++ ++ $N.isInstanceOf = function(obj, className) { ++ if (typeof obj == 'object' && obj.__class__) { ++ var c = obj.__class__; ++ if (c.__name__ == className) { ++ return true; ++ } ++ while (c = c.__super__) { ++ if (c.__name__ == className) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ ++ $N.Class = { ++ create: function() { ++ var parent = null, className = null; ++ var properties = $N.argsToArray(arguments) ++ if (typeof properties[0] == "string"){ ++ className = properties.shift(); ++ } ++ if (typeof properties[0] == "function"){ ++ parent = properties.shift(); ++ } ++ ++ function klass() { ++ this.initialize.apply(this, arguments); ++ } ++ //Merge the contents of $N.Class.Methods into klass: ++ $N.extend(klass, $N.Class.Methods); ++ klass.__name__ = className; ++ klass.__super__ = parent; ++ klass.__subclasses__ = []; ++ ++ if (parent) { ++ var subclass = function() { }; ++ subclass.prototype = parent.prototype; ++ klass.prototype = new subclass; ++ parent.__subclasses__.push(klass); ++ } ++ ++ klass.prototype.__class__ = klass; ++ for (var i = 0; i < properties.length; i++){ ++ klass.addMethods(properties[i]); ++ } ++ ++ if (!klass.prototype.initialize){ ++ klass.prototype.initialize = function () {}; ++ ++ } ++ ++ klass.prototype.constructor = klass; ++ ++ if (className) { ++ $N[className] = klass; ++ } ++ return klass; ++ } ++ }; ++ ++ $N.Class.Methods = { ++ addMethods: function(source) { ++ var ancestor = this.__super__ && this.__super__.prototype; ++ var properties = $N.objectKeys(source); ++ ++ if (!$N.objectKeys({ ++ toString: true ++ }).length){ ++ properties.push("toString", "valueOf"); ++ } ++ for (var i = 0, length = properties.length; i < length; i++) { ++ var property = properties[i], value = source[property]; ++ if (ancestor && (typeof value == 'function') && ++ $N.argumentNames(value)[0] == "$super") { ++ var method = value; ++ value = $N.wrapFunction(method, (function(m) { ++ return function() { ++ return ancestor[m].apply(this, arguments) ++ }; ++ })(property)); ++ ++ value.valueOf = $N.attachFunction(method, method.valueOf); ++ value.toString = $N.attachFunction(method, method.toString); ++ } ++ this.prototype[property] = value; ++ } ++ ++ return this; ++ } ++ }; ++ ++ $N.Core = $N.Class.create("Core", { ++ eventContainer: null, ++ eventPrefix: '', ++ cfg: {}, ++ ++ initialize: function() { ++ this.debug("new instance"); ++ $N.registerInstance(this); ++ this.eventContainer = $J('
'); ++ this.forwardEvent = this.attach(this._forwardEvent); ++ }, ++ ++ free: function() { ++ this.eventContainer = null; ++ }, ++ ++ configure: function(config, defaults) { ++ if (!config){ ++ config = {}; ++ ++ } ++ for (k in defaults) { ++ var value = null, flags = []; ++ ++ if (defaults[k] && typeof defaults[k][0] !== 'undefined') { ++ value = defaults[k][0]; ++ if (defaults[k][1]) { ++ flags = defaults[k][1].split(","); ++ } ++ } else { ++ value = defaults[k]; ++ } ++ ++ if (typeof config[k] !== 'undefined'){ ++ value = config[k]; ++ } ++ ++ var source = this; ++ $J(flags).each(function(i, flag) { ++ switch (flag) { ++ case 'required': ++ if (value === null) ++ throw new $N.RequiredOptionError(source, k); ++ break; ++ /* ++ case 'element': ++ value = $J(value); ++ break; ++ */ ++ ++ } ++ }); ++ ++ this.cfg[k] = value; ++ } ++ return this; ++ }, ++ ++ observe: function(eventName, handler) { ++ this.eventContainer.bind(this.eventPrefix + eventName, handler); ++ return this; ++ }, ++ ++ fire: function(eventName, data) { ++ if (!data){ ++ data = {}; ++ ++ } ++ this.eventContainer.trigger(this.eventPrefix + eventName, data); ++ return this; ++ }, ++ ++ _forwardEvent: function(e, data) { ++ if (!data){ ++ data = {}; ++ ++ } ++ this.eventContainer.trigger(e.type, data); ++ return this; ++ }, ++ ++ _textWidth: function(text, fontSize) { ++ var ratio = 3/5; ++ return text.length * ratio * fontSize; ++ }, ++ ++ debug: function(message) { ++ if ($N.debugging && typeof console != 'undefined' && console.log) { ++ console.log('TimeSide.' + this.__class__.__name__ + ': ' + message); ++ } ++ }, ++ ++ attach: function(method) { ++ return $N.attachFunction(this, method); ++ }, ++ ++ attachWithEvent: function(method) { ++ return $N.attachAsEventListener(this, method); ++ }, ++ ++ uniqid: function() { ++ d = new Date(); ++ return new String(d.getTime() + '' + Math.floor(Math.random() * 1000000)).substr(0, 18); ++ } ++ }); ++ ++ $N.Class.create("Exception", { ++ _source: null, ++ _message: null, ++ ++ initialize: function(source, message) { ++ this._source = source; ++ this._message = message; ++ }, ++ toString: function() { ++ return this.__class__.__name__ + " from TimeSide." + this._source.__class__.__name__ ++ + ": " + this._message; ++ } ++ }); ++ ++ $N.Class.create("RequiredOptionError", $N.Exception, { ++ initialize: function($super, source, optionName) { ++ $super(source, "missing '" + optionName + "' required option"); ++ } ++ }); ++ ++ $N.Class.create("RequiredArgumentError", $N.Exception, { ++ initialize: function($super, source, optionName) { ++ $super(source, "missing '" + optionName + "' required argument"); ++ } ++ }); ++ ++ $N.notifyScriptLoad(); ++ ++}); diff --cc telemeta/htdocs/timeside/js/markerlist.js index 00000000,00000000..52bd1d4f new file mode 100755 --- /dev/null +++ b/telemeta/htdocs/timeside/js/markerlist.js @@@ -1,0 -1,0 +1,32 @@@ ++/** ++ * TimeSide - Web Audio Components ++ * Copyright (c) 2008-2009 Samalyse ++ * Author: Olivier Guilyardi ++ * License: GNU General Public License version 2.0 ++ */ ++ ++TimeSide(function($N) { ++ ++$N.Class.create("MarkerList", $N.Core, { ++ initialize: function($super, cfg) { ++ $super(); ++ this.cfg = this.configure(cfg, { ++ container: null, ++ map: null ++ }); ++ }, ++ ++ _buildItem: function(marker) { ++ var dt = new Element('dt'); ++ var time = $N.Util.makeTimeLabel(marker.offset); ++ ++ ++ }, ++ ++ _setupInterface: function() { ++ } ++}); ++ ++$N.notifyScriptLoad(); ++ ++}); diff --cc telemeta/htdocs/timeside/js/soundprovider.js index 00000000,00000000..d51959c0 new file mode 100644 --- /dev/null +++ b/telemeta/htdocs/timeside/js/soundprovider.js @@@ -1,0 -1,0 +1,197 @@@ ++/** ++ * TimeSide - Web Audio Components ++ * Copyright (c) 2008-2009 Samalyse ++ * Author: Olivier Guilyardi ++ * License: GNU General Public License version 2.0 ++ */ ++ ++TimeSide(function($N) { ++ ++ $N.Class.create("SoundProvider", $N.Core, { ++ sound: null, ++ timer: null, ++ buggyPosition: null, ++ isDurationForced: false, ++ state: { ++ position: null, ++ duration: null, ++ playing: false, ++ buffering: false ++ }, ++ lastState: null, ++ ++ initialize: function($super, cfg) { ++ $super(); ++ this.configure(cfg, { ++ source: null, ++ duration: null ++ }); ++ this.sound = this.cfg.source; ++ if (this.cfg.duration) { ++ this.forceDuration(this.cfg.duration); ++ } ++ this.state.position = 0; ++ this.update = this.attach(this._update); ++ this.timer = setInterval(this.update, 43); ++ this.init=true; ++ this._update(); ++ this.init=false; ++ }, ++ ++ free: function($super) { ++ this.sound = null; ++ $super(); ++ }, ++ ++ play: function() { ++ if (this.sound) { ++ //it seems that, if sound is played until its end ++ //playing sound again resets the volume to 100, even though ++ //sound.volume is at the right value. We use this trick which seems to work: ++ this.sound.setVolume(this.sound.volume); ++ if (!this.sound.playState) { ++ this.sound.play(); ++ //console.log(this.getVolume()); ++ } else if (this.sound.paused) { ++ this.sound.resume(); ++ } ++ } ++ return this; ++ }, ++ ++ pause: function() { ++ if (this.sound){ ++ this.sound.pause(); ++ } ++ return this; ++ }, ++ ++ setVolume: function(volume) { ++ if(typeof volume != 'number'){ ++ return this; ++ } ++ volume = volume<0 ? 0 : volume >100 ? 100 : parseInt(volume); ++ if (this.sound && this.sound.volume!==volume){ ++ this.sound.setVolume(volume); ++ } ++ this.fire('volume',{'volume':volume}); ++ return this; ++ }, ++ ++ getVolume: function() { ++ return this.sound.volume; ++ }, ++ ++ seek: function(offset) { ++ if (this.sound) { ++ this.sound.setPosition(offset * 1000); ++ if (!this.state.playing) { ++ //it's not always a number. When not playing it is a string ++ //(sound manager?) ++ var offs = typeof offset == "number" ? offset : parseFloat(offset); ++ this.buggyPosition = this.sound.position / 1000; ++ this.state.position = offs; ++ } ++ } ++ return this; ++ }, ++ ++ isPlaying: function() { ++ return this.state.playing; ++ }, ++ ++ getPosition: function() { ++ if (this.state.position == null){ ++ this._retrieveState(); ++ } ++ return this.state.position; ++ }, ++ ++ getDuration: function() { ++ if (this.state.duration == null){ ++ this._retrieveState(); ++ } ++ return this.state.duration; ++ }, ++ ++ forceDuration: function(duration) { ++ this.state.duration = duration; ++ this.isDurationForced = true; ++ }, ++ ++ isBuffering: function() { ++ return this.state.buffering; ++ }, ++ ++ _retrieveState: function() { ++ if (this.sound) { ++ this.state.playing = (this.sound.playState && !this.sound.paused); ++ if (this.state.playing) { ++ var position = this.sound.position / 1000; ++ if (position != this.buggyPosition) { ++ this.state.position = position; ++ this.buggyPosition = null; ++ } ++ } ++ if (!this.isDurationForced) { ++ if (this.sound.readyState == 1) { ++ this.state.duration = this.sound.durationEstimate / 1000; ++ } else { ++ this.state.duration = this.sound.duration / 1000; ++ } ++ } ++ this.state.buffering = (this.sound.readyState == 1 && this.state.position > this.sound.duration / 1000); ++ } ++ }, ++ ++ _update: function() { ++ this._retrieveState(); ++ var updated = false; ++ var k; ++ if (this.lastState) { ++ for (k in this.state) { ++ if (this.state[k] != this.lastState[k]) { ++ updated = true; ++ break; ++ } ++ } ++ } else { ++ this.lastState = {}; ++ updated = true; ++ } ++ if (updated) { ++ var fireevent = false; ++ for (k in this.state) { ++ if(k=='position' && this.lastState[k]!==undefined && this.lastState[k]!=this.state[k]){ ++ //this.lastState[k]!==undefined because otherwise we are initializing ++ this.debug('fire-play-stop'); ++ fireevent = true; ++ }else if(k=='playing' && this.lastState[k] && !this.state[k]){ ++ //stop playing, fire again to move the cursor REALLY at the end ++ this.debug('fire-stop'); ++ fireevent = true; ++ } ++ if(this.state[k] != this.lastState[k]){ ++ this.debug('(soundprovider _update) '+k+': oldVal: '+this.lastState[k]+': val: '+this.state[k]); ++ } ++ this.lastState[k] = this.state[k]; ++ ++ } ++ if(fireevent){ ++ this.debug('firing'); ++ this.fire('update'); ++ } ++ } ++ }, ++ ++ setSource: function(source) { ++ this.debug("setting source"); ++ this.sound = source; ++ return this; ++ } ++ ++ }); ++ ++ $N.notifyScriptLoad(); ++ ++}); diff --cc telemeta/htdocs/timeside/js/util.js index 00000000,00000000..221eff1a new file mode 100755 --- /dev/null +++ b/telemeta/htdocs/timeside/js/util.js @@@ -1,0 -1,0 +1,230 @@@ ++/** ++ * TimeSide - Web Audio Components ++ * Copyright (c) 2008-2009 Samalyse ++ * Author: Olivier Guilyardi ++ * License: GNU General Public License version 2.0 ++ */ ++ ++TimeSide(function($N, $J) { ++ ++ $N.Util = { ++ _loadChild: function(container, tag, className, index, contents) { ++ var p = $N.cssPrefix; ++ var element = container.find('.' + p + className); ++ if (!element.length) { ++ element = $J(document.createElement(tag)).addClass(p + className); ++ if (contents[className]) { ++ element.text(contents[className]); ++ } ++ var children = container.children(); ++ if (index < children.length) { ++ children.eq(index).before(element); ++ } else { ++ container.append(element); ++ } ++ } ++ return element; ++ }, ++ ++ _loadUI: function(container, skeleton, contents) { ++ var i = 0; ++ var elements = {}; ++ with ($N.Util) { ++ if (skeleton[0]) { ++ $J(skeleton).each((function(i, selector) { ++ var s = selector.split('.'); ++ elements[$N.Util.camelize(s[1])] = _loadChild(container, s[0], s[1], i++, contents); ++ })); ++ } else { ++ for (key in skeleton) { ++ var s = key.split('.'); ++ var e = _loadChild(container, s[0], s[1], i++, contents); ++ elements[$N.Util.camelize(s[1])] = e; ++ $N.extend(elements, _loadUI(e, skeleton[key], contents)); ++ ++ } ++ } ++ } ++ return elements; ++ }, ++ ++ loadUI: function(container, skeleton, contents) { ++ return $N.Util._loadUI($J(container), skeleton, contents); ++ }, ++ ++ makeTimeLabel: function(offset) { ++ var minutes = Math.floor(offset / 60); ++ if (minutes < 10) ++ minutes = '0' + minutes; ++ var seconds = Math.floor(offset % 60); ++ if (seconds < 10) ++ seconds = '0' + seconds; ++ return minutes + ':' + seconds; ++ }, ++ ++ camelize: function(str) { ++ var parts = str.split('-'), len = parts.length; ++ if (len == 1) return parts[0]; ++ ++ var camelized = str.charAt(0) == '-' ++ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) ++ : parts[0]; ++ ++ for (var i = 1; i < len; i++) ++ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); ++ ++ return camelized; ++ }, ++ ++ setUpTabs:function(selIndex) {//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 ++ // ++ ++ //declare variables: ++ var tabContainerHeight = '5ex'; //height for the tab container ++ var tabHeight = '3.5ex'; //height for the tab. Must be lower than tabContainerHeight ++ var tabPaddingTop ='.8ex'; //padding top of each tab. Increasing it will increase also the tab height, so ++ //compensate by decreasing tabHeight, in case. In any case, must be lower or equal to tabContainerHeight-tabHeight ++ var tabWidth = '12ex'; //width of each tab. Each tab from index 1 to n will be at left=n*tabWidth ++ var tabBottom ='-1px'; //bottom of each tab. Must be equal and opposite to the border of the div below the tab ++ ++ //retrieve tab container: ++ var tabContainer = $("#tabs_container"); //change if tabContainer has to be retrieved diferently ++ //retrieve the tabs by checking the elements whose class name starts with "tab_" ++ //var tabs = $('a[class^="tab_"]'); //change if the tabs have to be determined differently. ++ var tabs = tabContainer.find('a[id^="tab_"]'); ++ //function that retrieves the div relative to a tab (the div will be set visible.invisible according to tab click): ++ var tab2div = function(tab){ ++ return $("#"+tab.attr("name")); ++ //ie, returns the element whose id is equal to the tab name. ++ //change here if div has to be determined differently ++ }; ++ var selectedTabClassName = "tab_selected"; //change if needed ++ var unselectedTabClassName = "tab_unselected"; //change if needed ++ var tabClicked = function(index) { ++ for(var i=0; i