]> git.parisson.com Git - telemeta.git/commitdiff
merge from unstable r847
authoryomguy <yomguy@parisson.com>
Mon, 16 May 2011 17:04:08 +0000 (19:04 +0200)
committeryomguy <yomguy@parisson.com>
Mon, 16 May 2011 17:04:08 +0000 (19:04 +0200)
1  2 
telemeta/htdocs/timeside/js/controller.js
telemeta/htdocs/timeside/js/core.js
telemeta/htdocs/timeside/js/markerlist.js
telemeta/htdocs/timeside/js/soundprovider.js
telemeta/htdocs/timeside/js/util.js

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0683a511d42391fe3ff8aa5bebfc499c594d2e99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,164 @@@
++/**
++ * 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) {
++
++    $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(to<from){
++                var tmp = to;
++                to=from;
++                from=tmp;
++            }
++            for(var i = from; i <= to; i++){
++                this.cfg.divmarkers[i].updateMarkerIndex(i);
++            }
++            this.cfg.player.ruler.updateMarkerIndices(from,to);
++        },
++
++
++        loadHTTP: function(){
++            var itemId = ITEM_PUBLIC_ID;
++            var map = this.cfg.map;
++            var updateIndices = this.updateIndices;
++            //var setTabs = $N.Util.setUpTabs;
++            var util = $N.Util;
++            var me = this;
++            var onSuccess = function(data) {
++                    var tabIndex = 0;
++                    if(data){
++                        if(data.result && data.result.length>0){
++                            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();
++
++});
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e86b691766f0e2a521fd0ccdfdb9c9f4d65bec51
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,293 @@@
++/**
++ * 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.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('<div/>');
++            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();
++
++});
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52bd1d4f21d1b865a15222982e92fb77d3e44548
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,32 @@@
++/**
++ * 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) {
++
++$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();
++
++});
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d51959c0394a22540c4bb97e90fec85daaa6c6d7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,197 @@@
++/**
++ * 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) {
++
++    $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();
++
++});
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..221eff1a1169a208eecc885bf10002d6b8a51655
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,230 @@@
++/**
++ * 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.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<tabs.length; i++){
++                    var t = $(tabs[i]);
++                    if(i===index){
++                        t.removeClass(unselectedTabClassName).addClass(selectedTabClassName);
++                        tab2div(t).fadeIn('slow');
++                    }else{
++                        t.removeClass(selectedTabClassName).addClass(unselectedTabClassName);
++                        tab2div(t).hide();
++                    }
++                }
++                return false; //returning false avoids scroll of the anchor to the top of the page
++            //if the tab is an anchor, of course
++            };
++            //end of variables declaration
++
++
++            //first of all, delete the span shown only onloading
++            var span = tabContainer.find('#loading_span');
++            if(span.length){
++                span.remove();
++                consolelog('removed span');
++            }
++
++
++            //tabContainer default css:
++            tabContainer.css({
++                'position':'relative',
++                'height':tabContainerHeight
++            });
++            //tabs default css:
++            tabs.css({
++                'display':'', //reset the default value
++                'position':'absolute',
++                'height':tabHeight,
++                'bottom':tabBottom,
++                'paddingTop':tabPaddingTop,
++                'width':tabWidth,
++                'color': '#000000',
++                'left':0, //this will be overridden for tabs from 1 to n (see below)
++                'textAlign':'center'
++            });
++            //setting the left property for all tabs from 1 to n
++            var left = parseFloat(tabWidth); //note that 40%, 33.3ex will be converted
++            //succesfully to 40 and 33.3 respectively
++            if(!isNaN(left)){
++                //retrieve the unit
++                var s = new String(left);
++                var unit = '';
++                if(s.length<tabWidth.length){
++                    unit = tabWidth.substring(s.length,tabWidth.length);
++                }
++                for(var i=1; i<tabs.length; i++){
++                    $(tabs[i]).css('left',(left*i)+unit);
++                }
++            }
++
++            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){
++                    $(tabs[i]).click(function(){
++                        return tabClicked(tabIndex);
++                    });
++                })(i);
++            }
++
++            this.setRoundBorder(tabs,'5px','5px',[0,1]);
++
++            if(!(selIndex)){
++                selIndex = 0;
++            }
++            $(tabs[selIndex]).trigger("click");
++        },
++
++        selectMarkerTab: function(){
++            $('#tab_markers').trigger("click");
++        },
++        //set cross browser round borders.
++        //elements: the html element or elements (jQuery syntax)
++        //hRadius the horizontal radius, or the horizontal vertical radius if the latter is omitted (see below)
++        //vRadius OPTIONAL the vertical radius. If missing, it defaults to hRadius
++        //angles: OTPIONAL. An array object of the corner indices where to apply radius. Indices are
++        //      considered clockwise starting from the top left corner,ie:
++        //      0=topleft, 1 topright, 2 bottomright, 3 bottomleft
++        //      If missing, it defaults to [0,1,2,3] (all indices)
++        setRoundBorder: function(elements, hRadius, vRadius, whichAngles){
++            if(!(vRadius)){
++                vRadius = hRadius;
++            }
++            var cssVal = hRadius+' '+vRadius;
++            if(!(whichAngles)){
++                whichAngles = [0,1,2,3];
++            }
++            $(elements).each(function(){
++                var element = $(this);
++                for(var i=0; i<whichAngles.length; i++){
++                    var keys=[];
++                    if(whichAngles[i]===0){
++                        keys=['-webkit-border-top-left-radius','moz-border-radius-topleft','border-top-left-radius'];
++                    }else if(whichAngles[i]===1){
++                        keys=['-webkit-border-top-right-radius','moz-border-radius-topright','border-top-right-radius'];
++                    }else if(whichAngles[i]===2){
++                        keys=['-webkit-border-bottom-right-radius','moz-border-radius-bottomright','border-bottom-right-radius'];
++                    }else if(whichAngles[i]===3){
++                        keys=['-webkit-border-bottom-left-radius','moz-border-radius-bottomleft','border-bottom-left-radius'];
++                    }
++                    if(keys){
++                        for(var j=0; j<keys.length; j++){
++                            element.css(keys[j],cssVal);
++                        }
++                    }
++                //            element.css('-webkit-border-top-left-radius',hRadius+' '+vRadius);
++                //            element.css('moz-border-radius-topleft',hRadius+' '+vRadius);
++                //            element.css('border-top-left-radius',hRadius+' '+vRadius);
++                }
++            });
++
++        }
++        
++
++    }
++
++    $N.notifyScriptLoad();
++
++});