From d8f6997ba14618b5f06aaf2e60bd0513fa03506f Mon Sep 17 00:00:00 2001 From: riccardo Date: Wed, 9 Feb 2011 15:27:33 +0100 Subject: [PATCH] till work in progress --- telemeta/htdocs/js/player.js | 102 --- telemeta/htdocs/timeside/src/controller.js | 8 +- telemeta/htdocs/timeside/src/core.js | 2 +- telemeta/htdocs/timeside/src/marker.js | 385 ++++++----- telemeta/htdocs/timeside/src/player.js | 202 +++--- telemeta/htdocs/timeside/src/ruler.js | 642 +++++++++--------- telemeta/htdocs/timeside/src/timeside.js | 189 +++--- .../telemeta_default/mediaitem_detail.html | 132 ++-- 8 files changed, 810 insertions(+), 852 deletions(-) delete mode 100644 telemeta/htdocs/js/player.js diff --git a/telemeta/htdocs/js/player.js b/telemeta/htdocs/js/player.js deleted file mode 100644 index 4ff463d1..00000000 --- a/telemeta/htdocs/js/player.js +++ /dev/null @@ -1,102 +0,0 @@ -var sound = null; -var soundUrl = null; -var soundEngineReady = false; -var map; -var provider; -var player; -var player_image_url = null; - -function togglePlayerMaximization() { - var view = $('#player'); - $('#player_maximized, #player_minimized').css('display', 'none'); - var ctr; - if (view.parents('#player_maximized').length) { - ctr = $('#player_minimized').append(view); - } else { - ctr = $('#player_maximized').append(view); - } - ctr.css({opacity: 0, display: 'block'}); - if (player) - player.resize(); - ctr.animate({opacity: 1}, 100); -} - -function load_sound() { - if (!sound && soundUrl && soundEngineReady) { - sound = soundManager.createSound({ - id: 'sound', - url: soundUrl - }); - - TimeSide.load(function() { - provider.setSource(sound); - }); - // sound.load(); // Auto-loading overloads the Django test server - } -} - -function change_visualizer() { - set_player_image_url($('#visualizer_id').get(0).value); - if (player) - player.refreshImage(); - return false; -} - -function load_player(duration) { - $(document).ready(function () { - if (!$('#player').length) - return; - - 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: duration}); - player = new TimeSide.Player('#player', { - image: get_player_image_src - }); - controller = new TimeSide.Controller({ - player: player, - soundProvider: provider, - map: map - }); - change_visualizer(); - player.resize(); - }); - - $('#visualizer_id').change(change_visualizer); - $('#visualizer_id_form').submit(change_visualizer); - - $('#player_maximized .toggle, #player_minimized .toggle').click(function() { - togglePlayerMaximization(); - this.blur(); - return false; - }); - - load_sound(); - }); - - soundManager.onload = function() { - soundEngineReady = true; - load_sound(); - } - -} - -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; -} - - - diff --git a/telemeta/htdocs/timeside/src/controller.js b/telemeta/htdocs/timeside/src/controller.js index 0ed4989f..ff056256 100644 --- a/telemeta/htdocs/timeside/src/controller.js +++ b/telemeta/htdocs/timeside/src/controller.js @@ -33,16 +33,11 @@ TimeSide(function($N) { .observe('move', this.attach(this._onMove)) .observe('markeradd', this.attach(this._onMarkerAdd)) .observe('markermove', this.attach(this._onMarkerMove)) - .observe('markeradd2',this.attach(this._onMarkerAdd2)) + .draw(); }, - _onMarkerAdd2: function(e,data){ - if (this.cfg.map) { - alert(this.cfg.map._toString()); - } - }, _onMove: function(e, data) { this.cfg.soundProvider.seek(data.offset); @@ -77,6 +72,7 @@ TimeSide(function($N) { this.refreshMarkersText(this.cfg.map); this.cfg.map.add(data.offset, ''); this.updateMarkersDiv(this.cfg.map, data.offset); + } }, diff --git a/telemeta/htdocs/timeside/src/core.js b/telemeta/htdocs/timeside/src/core.js index 02c9ab4a..e86b6917 100644 --- a/telemeta/htdocs/timeside/src/core.js +++ b/telemeta/htdocs/timeside/src/core.js @@ -93,7 +93,7 @@ TimeSide(function($N, $J) { 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; diff --git a/telemeta/htdocs/timeside/src/marker.js b/telemeta/htdocs/timeside/src/marker.js index c6759162..2a904b73 100644 --- a/telemeta/htdocs/timeside/src/marker.js +++ b/telemeta/htdocs/timeside/src/marker.js @@ -7,55 +7,55 @@ TimeSide(function($N, $J) { -$N.Class.create("Marker", $N.Core, { - id: null, - painter: null, - visible: false, - position: 0, - label: null, - blinking: false, - nodes: null, - mouseDown: false, - blinkAnimation: null, - - initialize: function($super, cfg) { - $super(); - this.configure(cfg, { - rulerLayout: [null, 'required'], - viewer: [null, 'required'], - fontSize: 10, - zIndex: null, - className: [null, 'required'], - id: null, - tooltip: null - }); - this.cfg.rulerLayout = $J(this.cfg.rulerLayout); - this.cfg.viewer = $J(this.cfg.viewer); - - this.id = cfg.id; - this.width = this.cfg.viewer.width(); - this.painter = new jsGraphics(this.cfg.viewer.get(0)); - this._create(); - this._observeMouseEvents(); - }, - - 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(); - this.label = $J('') + $N.Class.create("Marker", $N.Core, { + id: null, + painter: null, + visible: false, + position: 0, + label: null, + blinking: false, + nodes: null, + mouseDown: false, + blinkAnimation: null, + + initialize: function($super, cfg) { + $super(); + this.configure(cfg, { + rulerLayout: [null, 'required'], + viewer: [null, 'required'], + fontSize: 10, + zIndex: null, + className: [null, 'required'], + id: null, + tooltip: null + }); + this.cfg.rulerLayout = $J(this.cfg.rulerLayout); + this.cfg.viewer = $J(this.cfg.viewer); + + this.id = cfg.id; + this.width = this.cfg.viewer.width(); + this.painter = new jsGraphics(this.cfg.viewer.get(0)); + this._create(); + this._observeMouseEvents(); + }, + + 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(); + this.label = $J('') .css({ display: 'block', width: '10px', @@ -70,155 +70,178 @@ $N.Class.create("Marker", $N.Core, { .append('') .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]; - var 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 = ''; + 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]; + var 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) + this.nodes.hide().css(style).addClass($N.cssPrefix + this.cfg.className) .each(function(i, node) { node.originalPosition = parseInt($J(node).css('left')); }); - }, - - 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 }); + }, + + 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); } - this.label.find('span').html(text); - } - return this; - }, - - move: function(pixelOffset) { - if (this.position != pixelOffset) { - if (pixelOffset < 0) { - pixelOffset = 0; - } else if (pixelOffset >= this.width) { - pixelOffset = this.width - 1; + return this; + }, + + move: function(pixelOffset) { + if (this.position != pixelOffset) { + if (pixelOffset < 0) { + pixelOffset = 0; + } else if (pixelOffset >= this.width) { + pixelOffset = this.width - 1; + } + this.nodes.each(function(i, node) { + $J(node).css('left', Math.round(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; } - this.nodes.each(function(i, node) { - $J(node).css('left', Math.round(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; - } - return this; - }, - - show: function(offset) { - if (!this.visible) { - this.nodes.show(); - this.label.show(); - this.visible = true; - } - 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) }); + return this; + }, + + show: function(offset) { + if (!this.visible) { + this.nodes.show(); + this.label.show(); + this.visible = true; + } + 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: 0.4}, speed, null, - function() { fade(true) }) + span.animate({ + opacity: 1 + }, speed); } - } - if (state) { - fade(); - } else { - span.animate({opacity: 1}, speed); + this.blinking = state; } + return this; + }, - this.blinking = state; - } - return this; - }, - - _onMouseDown: function(evt) { - this.mouseDown = true; - this._onMouseMove(evt); - return false; - }, - - _onMouseMove: function(evt) { - if (this.mouseDown) { - var offset = (evt.pageX - this.cfg.rulerLayout.offset().left); - this.move(offset); - this.fire('move', {offset: this.position, finish: false}); + _onMouseDown: function(evt) { + this.mouseDown = true; + this._onMouseMove(evt); return false; - } - }, + }, + + _onMouseMove: function(evt) { + if (this.mouseDown) { + var offset = (evt.pageX - this.cfg.rulerLayout.offset().left); + this.move(offset); + this.fire('move', { + offset: this.position, + finish: false + }); + return false; + } + }, + + _onMouseUp: function(evt) { + if (this.mouseDown) { + this.mouseDown = false; + this.fire('move', { + id: this.id, + offset: this.position, + finish: true + }); + return false; + } + }, - _onMouseUp: function(evt) { - if (this.mouseDown) { - this.mouseDown = false; - this.fire('move', {id: this.id, offset: this.position, finish: true}); - return false; + _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)); } - }, - - _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 ""; -// } + // _toString: function() { + // return ""; + // } -}); + }); -$N.notifyScriptLoad(); + $N.notifyScriptLoad(); }); diff --git a/telemeta/htdocs/timeside/src/player.js b/telemeta/htdocs/timeside/src/player.js index a43baec3..e82bc19c 100644 --- a/telemeta/htdocs/timeside/src/player.js +++ b/telemeta/htdocs/timeside/src/player.js @@ -7,82 +7,82 @@ 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.set-marker2'] + $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.set-marker2'] //,'input.textMarker'] - } - }/*, + } + }/*, 'div.marker-control': ['a.set-marker']*/ - }, - defaultContents: { - play: 'Play', - pause: 'Pause', - rewind: 'Rewind', - forward: 'Forward', - 'set-marker': 'Set marker', - 'set-marker2': 'Set marker2' + }, + defaultContents: { + play: 'Play', + pause: 'Pause', + rewind: 'Rewind', + forward: 'Forward', + 'set-marker': 'Set marker' //,'text-marker' : 'textmarker' - }, - elements: {}, - ruler: null, - soundProvider: 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 - }); - }, + }, + elements: {}, + ruler: null, + soundProvider: 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 + }); + }, - free: function($super) { - this.elements = null; - this.container = null; - $super(); - }, + free: function($super) { + this.elements = null; + this.container = null; + $super(); + }, - setSoundProvider: function(soundProvider) { - this.soundProvider = soundProvider; - return this; - }, + setSoundProvider: function(soundProvider) { + this.soundProvider = soundProvider; + return this; + }, - setMarkerMap: function(map) { - this.map = map; - return this; - }, + setMarkerMap: function(map) { + this.map = map; + return this; + }, - setImage: function(expr) { - this.cfg.image = expr; - this.refreshImage(); - }, + 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; - } + 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; + } - if (src) - this.elements.image.attr('src', src); - }, + if (src) { + this.elements.image.attr('src', src); + } + }, draw: function() { this.debug('drawing'); @@ -96,42 +96,51 @@ $N.Class.create("Player", $N.Core, { // 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;}); + .click(function() { + return false; + }); this.elements.forward.attr('href', '#').bind(jump, this.attach(this._onForward)) - .click(function() {return false;}); + .click(function() { + return false; + }); this.elements.pause.attr('href', '#').bind('click', this.attach(this._onPause)); this.elements.play.attr('href', '#').bind('click', this.attach(this._onPlay)); //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()); - }); + .attr('href', '#') + .each(function(i, a){ + a = $J(a); + if (!a.attr('title')) + a.attr('title', a.text()); + }); //this.elements.markerControl.find('a').attr('href', '#'); if (this.map) { //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)); + //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 this.ruler = new $N.Ruler({ viewer: this.elements.viewer, map: this.map, soundProvider: this.soundProvider }); + //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(); + .observe('markermove', this.forwardEvent) + .observe('markeradd', this.forwardEvent) + .observe('move', this.forwardEvent) + .draw(); this.refreshImage(); this.resize(); var resizeTimer = null; @@ -140,14 +149,7 @@ $N.Class.create("Player", $N.Core, { clearTimeout(resizeTimer); resizeTimer = setTimeout(this.attach(this.resize), 100); })); - //this.container.resize(this.attach(this.resize)); // Can loop ? - }, - - _onSetMarker2: function() { - if (this.map) { - this.fire('markeradd2', {offset: this.soundProvider.getPosition()}); - } - return false; + //this.container.resize(this.attach(this.resize)); // Can loop ? }, resize: function(overrideHeight) { @@ -169,8 +171,8 @@ $N.Class.create("Player", $N.Core, { } var elements = this.elements.image - .add(this.elements.imageContainer) - .add(this.elements.imageCanvas); + .add(this.elements.imageContainer) + .add(this.elements.imageCanvas); elements.css('width', 'auto'); // for IE6 @@ -200,7 +202,9 @@ $N.Class.create("Player", $N.Core, { offset = marker.offset; } } - this.fire('move', {offset: offset}); + this.fire('move', { + offset: offset + }); return false; }, @@ -212,7 +216,9 @@ $N.Class.create("Player", $N.Core, { offset = marker.offset; } } - this.fire('move', {offset: offset}); + this.fire('move', { + offset: offset + }); return false; }, @@ -228,12 +234,14 @@ $N.Class.create("Player", $N.Core, { _onSetMarker: function() { if (this.map) { - this.fire('markeradd', {offset: this.soundProvider.getPosition()}); + this.fire('markeradd', { + offset: this.soundProvider.getPosition() + }); } return false; } -}); + }); $N.notifyScriptLoad(); -}); + }); diff --git a/telemeta/htdocs/timeside/src/ruler.js b/telemeta/htdocs/timeside/src/ruler.js index 5932c431..7ed9e64f 100644 --- a/telemeta/htdocs/timeside/src/ruler.js +++ b/telemeta/htdocs/timeside/src/ruler.js @@ -7,105 +7,105 @@ TimeSide(function($N, $J) { -$N.Class.create("Ruler", $N.Core, { + $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], + 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, - soundProvider: [null, 'required'] - }); - 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._setDuration(this.cfg.soundProvider.getDuration()); - var imgContainer = this.cfg.viewer.find('.' + $N.cssPrefix + 'image-container'); // for IE - this._observeMouseEvents(this.waveContainer.add(imgContainer)); - if (this.cfg.map) { - this.cfg.map + 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, + soundProvider: [null, 'required'] + }); + 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._setDuration(this.cfg.soundProvider.getDuration()); + var imgContainer = this.cfg.viewer.find('.' + $N.cssPrefix + 'image-container'); // for IE + this._observeMouseEvents(this.waveContainer.add(imgContainer)); + 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)); - }, - - 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.cfg.soundProvider.observe('update', this.attach(this._onSoundProviderUpdate)); + }, + + 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++) { - 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); - break; + 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++) { + 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); + break; + } } - } - }, + }, - getUnitDuration: function() { - return this.sectionSubDivision; - }, + getUnitDuration: function() { + return this.sectionSubDivision; + }, - resize: function() { - var pointerVisible = this.pointer && this.pointer.isVisible(); - this._computeLayout(); - this.draw(); - if (pointerVisible) { - this.setPosition(this.cfg.soundProvider.getPosition()); - this.setBuffering(this.cfg.soundProvider.isBuffering() && this.cfg.soundProvider.isPlaying()); - this.pointer.show(); - } - }, - - _setDuration: function(duration) { - this.duration = duration; - this._computeLayout(); - }, - - setDuration: function(duration) { - if (duration == 0) - duration = 60; - if (this.duration != duration) { - this._setDuration(duration); + resize: function() { + var pointerVisible = this.pointer && this.pointer.isVisible(); + this._computeLayout(); this.draw(); - } - }, + if (pointerVisible) { + this.setPosition(this.cfg.soundProvider.getPosition()); + this.setBuffering(this.cfg.soundProvider.isBuffering() && this.cfg.soundProvider.isPlaying()); + this.pointer.show(); + } + }, + + _setDuration: function(duration) { + this.duration = duration; + this._computeLayout(); + }, + + setDuration: function(duration) { + if (duration == 0) + duration = 60; + if (this.duration != duration) { + this._setDuration(duration); + this.draw(); + } + }, - _createSection: function(timeOffset, pixelWidth) { - var section = $J('
') + _createSection: function(timeOffset, pixelWidth) { + var section = $J('
') .addClass($N.cssPrefix + 'section') .css({ fontSize: this.cfg.fontSize + 'px', @@ -115,167 +115,179 @@ $N.Class.create("Ruler", $N.Core, { }) .append($J('
').addClass($N.cssPrefix + 'canvas')); - var topDiv = $J('
') + var topDiv = $J('
') .addClass($N.cssPrefix + 'label') .appendTo(section); - var bottomDiv = $J('
') + var bottomDiv = $J('
') .addClass($N.cssPrefix + 'lines') .appendTo(section); - var empty = $J('').css({visibility: 'hidden'}).text(' '); - if (pixelWidth > this.timeLabelWidth) { - var text = $J('') + var empty = $J('').css({ + visibility: 'hidden' + }).text(' '); + if (pixelWidth > this.timeLabelWidth) { + var text = $J('') .text($N.Util.makeTimeLabel(timeOffset)) - .bind('mousedown selectstart', function() {return false;}); - } else { - var text = empty.clone(); - } - topDiv.append(text); - bottomDiv.append(empty); - return section; - }, - - _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 height = section.height(); - var ypos; - for (j = 0; j < section.duration; j += this.sectionSubDivision) { - if (j == 0) { - if (drawFirstMark) { - ypos = 0; + .bind('mousedown selectstart', function() { + return false; + }); + } else { + var text = empty.clone(); + } + topDiv.append(text); + bottomDiv.append(empty); + return section; + }, + + _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 height = section.height(); + var ypos; + for (j = 0; j < section.duration; j += this.sectionSubDivision) { + if (j == 0) { + if (drawFirstMark) { + ypos = 0; + } else { + continue; + } } else { - continue; + ypos = (j == section.duration / 2) ? 1/2 + 1/8 : 3/4; } - } else { - ypos = (j == section.duration / 2) ? 1/2 + 1/8 : 3/4; + var x = j / this.duration * this.width; + jg.drawLine(x, height * ypos, x, height - 1); } - var x = j / this.duration * this.width; - jg.drawLine(x, height * ypos, x, height - 1); - } - jg.paint(); - }, + jg.paint(); + }, - getHeight: function() { - return this.container.find('' + $N.cssPrefix + '.section').height(); - }, + 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('
') + 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('
') .addClass($N.cssPrefix + 'layout') - .css({position: 'relative'}) // bugs on IE when resizing + .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); + //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 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'}); + } + 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; } - section.duration = duration; - this.layout.append(section); - currentWidth += section.width(); - sections[i] = section; - } - for (i = 0; i <= this.sectionsNum; i++) { - this._drawSectionRuler(sections[i], (i > 0)); - } + for (i = 0; i <= this.sectionsNum; i++) { + this._drawSectionRuler(sections[i], (i > 0)); + } - this._createPointer(); - this._drawMarkers(); - }, + this._createPointer(); + this._drawMarkers(); + }, - _createPointer: function() { - if (this.pointer) { - this.pointer.clear(); - } - this.pointer = new $N.Marker({ - rulerLayout: this.layout.get(0), - viewer: this.waveContainer, - fontSize: this.cfg.fontSize, - zIndex: 1000, - className: 'pointer', - tooltip: 'Move head' - }); - this.pointer + _createPointer: function() { + if (this.pointer) { + this.pointer.clear(); + } + this.pointer = new $N.Marker({ + rulerLayout: this.layout.get(0), + viewer: this.waveContainer, + fontSize: this.cfg.fontSize, + zIndex: 1000, + className: 'pointer', + tooltip: 'Move head' + }); + this.pointer .setText($N.Util.makeTimeLabel(0)) .observe('move', this.attach(this._onPointerMove)); - }, - - _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)); - })); - } - }, + }, + + _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)); + })); + } + }, - _movePointer: function(offset) { - if (offset < 0) - offset = 0; - else if (offset > this.duration) - offset = this.duration; + _movePointer: function(offset) { + if (offset < 0) + offset = 0; + else if (offset > this.duration) + offset = this.duration; - pixelOffset = offset / this.duration * this.width; - if (this.pointer) { - this.pointer.move(pixelOffset); - this.pointer.setText($N.Util.makeTimeLabel(offset)); - } - this.pointerPos = offset; - }, + pixelOffset = offset / this.duration * this.width; + if (this.pointer) { + this.pointer.move(pixelOffset); + this.pointer.setText($N.Util.makeTimeLabel(offset)); + } + this.pointerPos = offset; + }, - _setPosition: function(offset) { - this._movePointer(offset); - if (this.pointer) { - this.pointer.show(); - } - }, + _setPosition: function(offset) { + this._movePointer(offset); + if (this.pointer) { + this.pointer.show(); + } + }, - setPosition: function(offset) { - if (!this.mouseDown) { - this._setPosition(offset); - } - }, + setPosition: function(offset) { + if (!this.mouseDown) { + this._setPosition(offset); + } + }, - shiftPosition: function(delta) { - this.setPosition(this.pointerPos + delta); - }, + shiftPosition: function(delta) { + this.setPosition(this.pointerPos + delta); + }, - hidePointer: function() { - if (this.pointer) - this.pointer.hide(); - }, + hidePointer: function() { + if (this.pointer) + this.pointer.hide(); + }, - setBuffering: function(state) { - if (this.pointer) { - this.pointer.blink(state); - } - }, -/* + 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; @@ -283,116 +295,128 @@ $N.Class.create("Ruler", $N.Core, { this.fire('move', {offset: offset}); }, */ - _onMouseDown: function(evt) { - this.mouseDown = true; - this._onMouseMove(evt); - evt.preventDefault(); - }, - - _onPointerMove: function(evt, data) { - this.mouseDown = true; - this._setPosition(data.offset / this.width * this.duration); - if(data.finish) { - this.fire('move', {offset: this.pointerPos}); - this.mouseDown = false; - } - return false; - }, - - _onMouseMove: function(evt) { - if (this.mouseDown) { - var pixelOffset = evt.pageX - this.container.offset().left; - this._setPosition(pixelOffset / this.width * this.duration); + _onMouseDown: function(evt) { + this.mouseDown = true; + this._onMouseMove(evt); + evt.preventDefault(); + }, + + _onPointerMove: function(evt, data) { + this.mouseDown = true; + this._setPosition(data.offset / this.width * this.duration); + if(data.finish) { + this.fire('move', { + offset: this.pointerPos + }); + this.mouseDown = false; + } return false; - } - }, + }, - _onMouseUp: function(evt) { - if (this.mouseDown) { - this.mouseDown = false; - this.fire('move', {offset: this.pointerPos}); - return false; - } - }, + _onMouseMove: function(evt) { + if (this.mouseDown) { + var pixelOffset = evt.pageX - this.container.offset().left; + this._setPosition(pixelOffset / this.width * this.duration); + return false; + } + }, + + _onMouseUp: function(evt) { + if (this.mouseDown) { + this.mouseDown = false; + this.fire('move', { + offset: this.pointerPos + }); + return false; + } + }, - _observeMouseEvents: function(element) { - element - .bind('click dragstart', function() {return false;}) + _observeMouseEvents: function(element) { + 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) + $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; + _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; - - m = new $N.Marker({ - rulerLayout: this.layout.get(0), - viewer: this.waveContainer, - fontSize: this.cfg.fontSize, - className: 'marker', - id: marker.id, - tooltip: 'Move marker' - }); - m.observe('move', this.attach(this._onMarkerMove)) + pixelOffset = marker.offset / this.duration * this.width; + + m = new $N.Marker({ + rulerLayout: this.layout.get(0), + viewer: this.waveContainer, + fontSize: this.cfg.fontSize, + className: 'marker', + id: marker.id, + tooltip: 'Move marker' + }); + m.observe('move', this.attach(this._onMarkerMove)) .setText(index + 1) .move(pixelOffset) .show(); - return m; - }, + return m; + }, + + _onMarkerMove: function(e, data) { + if (data.finish) { + var offset = data.offset / this.width * this.duration; + this.fire('markermove', { + id: data.id, + offset: offset + }); + } + }, - _onMarkerMove: function(e, data) { - if (data.finish) { - var offset = data.offset / this.width * this.duration; - this.fire('markermove', {id: data.id, offset: offset}); - } - }, + _onMapAdd: function(e, data) { + this.markers.push(this._drawMarker(data.marker, data.index)); + }, - _onMapAdd: function(e, data) { - this.markers.push(this._drawMarker(data.marker, data.index)); - }, + _onMapRemove: function(e, data) { + $J(this.markers).each(this.attach(function(i, m) { + if (m.id == data.marker.id) { + m.clear(); + this.markers.splice(i, 1); + } + })); + }, - _onMapRemove: function(e, data) { - $J(this.markers).each(this.attach(function(i, m) { - if (m.id == data.marker.id) { - m.clear(); - this.markers.splice(i, 1); - } - })); - }, + _onMapIndexChange: function(e, data) { + $J(this.markers).each(this.attach(function(i, m) { + if (m.id == data.marker.id) { + m.setText(data.index + 1); + return false; + } + })); + }, - _onMapIndexChange: function(e, data) { - $J(this.markers).each(this.attach(function(i, m) { - if (m.id == data.marker.id) { - m.setText(data.index + 1); - return false; + _onDoubleClick: function(evt) { + if (this.cfg.map) { + var offset = (evt.pageX - this.container.offset().left) + / this.width * this.duration; + this.fire('markeradd', { + offset: offset + }); } - })); - }, + }, - _onDoubleClick: function(evt) { - if (this.cfg.map) { - var offset = (evt.pageX - this.container.offset().left) - / this.width * this.duration; - this.fire('markeradd', {offset: offset}); + _onSoundProviderUpdate: function(e) { + this.setDuration(this.cfg.soundProvider.getDuration()); + this.setPosition(this.cfg.soundProvider.getPosition()); + this.setBuffering(this.cfg.soundProvider.isBuffering() && this.cfg.soundProvider.isPlaying()); } - }, - - _onSoundProviderUpdate: function(e) { - this.setDuration(this.cfg.soundProvider.getDuration()); - this.setPosition(this.cfg.soundProvider.getPosition()); - this.setBuffering(this.cfg.soundProvider.isBuffering() && this.cfg.soundProvider.isPlaying()); - } -}); + }); -$N.notifyScriptLoad(); + $N.notifyScriptLoad(); }); diff --git a/telemeta/htdocs/timeside/src/timeside.js b/telemeta/htdocs/timeside/src/timeside.js index 95b0f517..dffefa18 100644 --- a/telemeta/htdocs/timeside/src/timeside.js +++ b/telemeta/htdocs/timeside/src/timeside.js @@ -5,120 +5,127 @@ * License: GNU General Public License version 2.0 */ -var TimeSide = function() { +//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') + 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 = false; - -$J(document).ready(function () { - $N.isDomLoaded = true; -}); + $N.isDomLoaded = false; + $N.isLoaded = false; + $N.isLoading = false; + $N.onLoadCallbacks = []; + $N.cssPrefix = 'ts-'; + $N.debugging = false; -$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); -} - -$N.instances = []; -$N.registerInstance = function(obj) { - $N.instances.push(obj); -} - -$N.free = function() { - $J($N.instances).each(function(i, obj) { - obj.free(); + $J(document).ready(function () { + $N.isDomLoaded = true; }); -} -$J(window).unload($N.free); + $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); + } + } -$N.loadScriptsNum = 0; -$N.loadScriptsCallback = null; -$N.loadScripts = function(root, scripts, callback) { - if ($N.loadScriptsCallback) { - throw "Timeside loader error: concurrent script loading"; + $N.instances = []; + $N.registerInstance = function(obj) { + $N.instances.push(obj); } - $N.loadScriptsNum = scripts.length; - $N.loadScriptsCallback = callback; + $N.free = function() { + $J($N.instances).each(function(i, obj) { + obj.free(); + }); + } + + $J(window).unload($N.free); + + $N.loadScriptsNum = 0; + $N.loadScriptsCallback = null; + $N.loadScripts = function(root, scripts, callback) { + if ($N.loadScriptsCallback) { + throw "Timeside loader error: concurrent script loading"; + } + + $N.loadScriptsNum = scripts.length; + $N.loadScriptsCallback = callback; - var head= document.getElementsByTagName('head')[0]; - for (i = 0; i < scripts.length; i++) { + var head= document.getElementsByTagName('head')[0]; + for (i = 0; i < scripts.length; i++) { - 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); + 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); + } } -} -$N.notifyScriptLoad = function() { - if (--$N.loadScriptsNum == 0 && $N.loadScriptsCallback) { - var callback = $N.loadScriptsCallback; - $N.loadScriptsCallback = null; - callback(); + $N.notifyScriptLoad = function() { + if (--$N.loadScriptsNum == 0 && $N.loadScriptsCallback) { + var callback = $N.loadScriptsCallback; + $N.loadScriptsCallback = null; + callback(); + } } -} - -$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 = ''; - $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', 'marker.js', 'markerlist.js', - 'markermap.js', 'player.js', 'ruler.js', - 'soundprovider.js']; + + $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 = ''; + $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', 'marker.js', 'markerlist.js', + 'markermap.js', 'player.js', 'ruler.js', + 'soundprovider.js']; - $N.loadScripts(root, scripts, function() { - $N.isLoaded = true; - $N.isLoading = false; - $J($N.onLoadCallbacks).each(function(i, callback) { - callback(); + $N.loadScripts(root, scripts, function() { + $N.isLoaded = true; + $N.isLoading = false; + $J($N.onLoadCallbacks).each(function(i, callback) { + callback(); + }); }); }); }); - }); + } } - } - }); -} + }); + } }); diff --git a/telemeta/templates/telemeta_default/mediaitem_detail.html b/telemeta/templates/telemeta_default/mediaitem_detail.html index 188bd537..974b6b18 100644 --- a/telemeta/templates/telemeta_default/mediaitem_detail.html +++ b/telemeta/templates/telemeta_default/mediaitem_detail.html @@ -7,23 +7,23 @@ {% block stylesheets %} {{ block.super }} - - -{% endblock %} -{% block extra_javascript %} + + + {% endblock %} + {% block extra_javascript %} - + {% endblock %} @@ -31,10 +31,10 @@ load_player({{ item.approx_duration.as_seconds }}); {% if item %} {% block submenu %} - {% endblock %} {% block content %} @@ -42,31 +42,33 @@ load_player({{ item.approx_duration.as_seconds }});

Item : {{ item }}

-{% if item.file %} + {% if item.file %}
- Maximize -
-
-
-
-
- - + Maximize +
+
+
+
+
+ + +
-
- +
-
+
+
+
{% endblock %} {% else %} -

No such item

+

No such item

{% endif %} -- 2.39.5