explnum_id = $explnum_id; } /** * Renvoie le code svg généré * @param boolean edit true pour activer la possibilité d'édition * @return string */ public function getSvg($edit = false) { $this->getDimensions(); $this->svg = ""; $this->getBackground(); $this->getTimeScale(); $this->getSpeakers($edit); $this->getSegments(); $this->getCursor(); $this->svg .= ""; return $this->svg; } /** * Définit le tableau des dimensions */ private function getDimensions() { global $explnum_associate_speakers_svg_height; $this->dimensions = array( 'totalWidth' => 1500, // Largeur totale // Dimensions fond 'backgroundPadding' => 2, // Padding du background // Dimensions de la barre de graduations 'scaleTextY' => 15, // Ordonnée du texte de la barre de graduations 'scaleTextFontSize' => 12, // Taille de police du texte de la barre de graduations 'scaleBottom' => 40, // Ordonnée de la base de la barre de graduations 'scaleTop' => 20, // Ordonnée du sommet des grandes barres 'scaleMiddle' => 30, // Ordonnée du sommet des petites barres // Dimensions locuteurs 'speakerLeft' => 5, // Abscisse de gauche de la colonne speaker 'speakerTop' => 42, // Ordonnée du haut de la colonne speaker 'speakerWidth' => 150, // Largeur de la colonne speaker 'speakerHeight' => $explnum_associate_speakers_svg_height, // Hauteur d'une case speaker 'speakerMarginBottom' => 2, // Marge entre chaque speaker 'speakerTextFontSize' => 12, // Taille de police de texte 'speakerTextX' => 3, // Abscisse de début de texte 'speakerTextY' => 17, // Ordonnée du texte 'speakerMarginRight' => 2, // Marge à droite ); } /** * Construit la barre de graduation */ private function getTimeScale() { if (!count($this->segments)) { $this->getDatas(); } global $explnum_associate_timescale_svg; global $explnum_associate_timescale_svg_posX; global $explnum_associate_timescale_svg_posY; global $explnum_associate_timescale_svg_width; global $explnum_associate_timescale_svg_height; global $msg; // Un champ texte pour donner l'unité de temps $this->svg .= ''.$msg['explnum_associate_minutes'].''; $timescaleSvg = ''.$explnum_associate_timescale_svg.''; $x = $this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + $this->dimensions['backgroundPadding']; // Calcul de la largeur totale disponible pour le ratio $availableWidth = $this->dimensions['totalWidth'] - ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + (2 * $this->dimensions['backgroundPadding'])); // Temps total $duration = $this->duration; // On cherche l'intervalle idéal $interval = 1; // Calcul de la largeur pour l'intervalle $widthForInterval = ($availableWidth*$interval*100) / $duration; while (($widthForInterval*2) < 15) { $interval = $interval*2; $widthForInterval = ($availableWidth*$interval*100) / $duration; } $tps = 0; $cpt = 0; while ($x < $this->dimensions['totalWidth']) { // On regarde le compteur pour savoir la taille de la barre et si on affiche le texte $width = 10 * $widthForInterval / $explnum_associate_timescale_svg_width; if (!$cpt) { $transform = 'translate('.$x.', '.($this->dimensions['scaleBottom'] - $this->dimensions['scaleTop']).') scale('.$width.', '.($this->dimensions['scaleTop'] / $explnum_associate_timescale_svg_height).') translate('.(0 - $explnum_associate_timescale_svg_posX).', '.(0 - $explnum_associate_timescale_svg_posY).')'; $currentTimescaleSvg = str_replace("!!transform!!", $transform, $timescaleSvg); $this->svg .= $currentTimescaleSvg; } if (!$cpt || $cpt == 5) { $this->svg .= ''.$this->getTimeToDisplay($tps).''; } $cpt++; if ($cpt == 10) $cpt = 0; $tps += $interval; $x += $widthForInterval; } } /** * Renvoie une chaine correspondant au temps à afficher (min:sec) * * @param int tps Temps en secondes */ private function getTimeToDisplay($tps) { $sec = $tps % 60; $min = ($tps - $sec) / 60; $sec = str_pad($sec, 2, '0', STR_PAD_LEFT); return $min.":".$sec; } /** * Construit les blocs locuteurs * @param boolean edit true pour activer la possibilité d'édition */ private function getSpeakers($edit) { if (!count($this->speakers)) { $this->getDatas(); } global $explnum_associate_speakers_svg; global $explnum_associate_speakers_svg_posX; global $explnum_associate_speakers_svg_posY; global $explnum_associate_speakers_svg_width; global $msg; $speakerSvg = ''.$explnum_associate_speakers_svg.''; foreach ($this->speakers as $id => $speaker) { $y = $speaker['posY']; $transform = "translate(".$this->dimensions['speakerLeft'].", ".$y.") scale(".($this->dimensions['speakerWidth'] / $explnum_associate_speakers_svg_width).", 1) translate(".(0 - $explnum_associate_speakers_svg_posX).", ".(0 - $explnum_associate_speakers_svg_posY).")"; $currentSpeakerSvg = str_replace("!!transform!!", $transform, $speakerSvg); $currentSpeakerSvg = str_replace("!!id!!", "speaker_svg_".$id, $currentSpeakerSvg); if ($edit) $no_author_message = $msg['explnum_associate_author']; else $no_author_message = $msg['explnum_associate_no_author']; $this->svg .= $currentSpeakerSvg.' '.($edit ? $speaker['id'] : '').' '.($speaker['author_libelle'] ? $speaker['author_libelle'] : $no_author_message).' '; if ($edit) { $this->svg .= ' '; } } $height = ($y + $this->dimensions['speakerHeight'] + $this->dimensions['speakerMarginBottom']); $this->svg = str_replace("!!height!!", $height, $this->svg); } /** * Construit les segments */ private function getSegments() { if (!count($this->segments)) { $this->getDatas(); } global $explnum_associate_segments_svg; global $explnum_associate_segments_svg_posX; global $explnum_associate_segments_svg_posY; global $explnum_associate_segments_svg_width; global $explnum_associate_segments_svg_height; $segmentSvg = ''.$explnum_associate_segments_svg.''; // Calcul de la largeur totale disponible pour le ratio $availableWidth = $this->dimensions['totalWidth'] - ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + (2 * $this->dimensions['backgroundPadding'])); // Temps total $duration = $this->duration; // Calcul ratio $ratio = $availableWidth / $duration; foreach ($this->segments as $id => $segment) { $x = $this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + $this->dimensions['backgroundPadding'] + ($segment['start'] * $ratio); $y = $this->speakers[$segment['speaker']]['posY']; $width = ($segment['duration'] * $ratio) / $explnum_associate_segments_svg_width; $transform = 'translate('.$x.', '.$y.') scale('.$width.', '.($this->dimensions['speakerHeight'] / $explnum_associate_segments_svg_height).') translate('.(0 - $explnum_associate_segments_svg_posX).', '.(0 - $explnum_associate_segments_svg_posY).')'; $currentSegmentSvg = str_replace("!!transform!!", $transform, $segmentSvg); $currentSegmentSvg = str_replace("!!id!!", "segment_svg_".$id, $currentSegmentSvg); $this->svg .= $currentSegmentSvg; } } /** * Consulte la base de données */ private function getDatas() { $query = "select explnum_speaker_id, explnum_speaker_speaker_num, explnum_speaker_gender, explnum_speaker_author, author_name, author_rejete from explnum_speakers left join authors on explnum_speaker_author = author_id where explnum_speaker_explnum_num = ".$this->explnum_id; $result = pmb_mysql_query($query); if ($result && pmb_mysql_num_rows($result)) { $i = 0; while ($speaker = pmb_mysql_fetch_object($result)) { $this->speakers[$speaker->explnum_speaker_id] = array( 'id' => $speaker->explnum_speaker_speaker_num, 'gender' => $speaker->explnum_speaker_gender, 'author' => $speaker->explnum_speaker_author, 'author_libelle' => $speaker->author_name.($speaker->author_rejete ? ', '.$speaker->author_rejete : ''), 'posY' => $this->dimensions['speakerTop'] + ($i * ($this->dimensions['speakerHeight'] + $this->dimensions['speakerMarginBottom'])) ); $i++; } } $query = "select explnum_segment_id, explnum_segment_speaker_num, explnum_segment_start, explnum_segment_duration, explnum_segment_end from explnum_segments where explnum_segment_explnum_num = ".$this->explnum_id; $result = pmb_mysql_query($query); if ($result && pmb_mysql_num_rows($result)) { while ($segment = pmb_mysql_fetch_object($result)) { $this->segments[] = array( 'db_id' => $segment->explnum_segment_id, 'speaker' => $segment->explnum_segment_speaker_num, 'start' => $segment->explnum_segment_start, 'duration' => $segment->explnum_segment_duration, 'end' => $segment->explnum_segment_end ); if ($segment->explnum_segment_end > $this->duration) $this->duration = $segment->explnum_segment_end; } } } /** * Construit le fond */ private function getBackground() { if (!count($this->speakers)) { $this->getDatas(); } global $explnum_associate_background_svg; global $explnum_associate_background_svg_posX; global $explnum_associate_background_svg_posY; global $explnum_associate_background_svg_width; global $explnum_associate_background_svg_height; $backgroundSvg = ''.$explnum_associate_background_svg.''; $x = ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight']); $y = $this->dimensions['scaleBottom']; $width = ($this->dimensions['totalWidth'] - ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'])) / $explnum_associate_background_svg_width; $height = ($this->dimensions['speakerTop'] - $this->dimensions['scaleBottom'] + count($this->speakers) * ($this->dimensions['speakerHeight'] + $this->dimensions['speakerMarginBottom'])) / $explnum_associate_background_svg_height; $transform = "translate(".$x.", ".$y.") scale(".$width.", ".$height.") translate(".(0 - $explnum_associate_background_svg_posX).", ".(0 - $explnum_associate_background_svg_posY).")"; $backgroundSvg = str_replace("!!transform!!", $transform, $backgroundSvg); $this->svg .= $backgroundSvg; } /** * Construit le curseur */ private function getCursor() { global $explnum_associate_cursor_svg; global $explnum_associate_cursor_svg_posX; global $explnum_associate_cursor_svg_posY; global $explnum_associate_cursor_svg_width; global $explnum_associate_cursor_svg_height; $cursorSvg = ''.$explnum_associate_cursor_svg.''; $x = ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + $this->dimensions['backgroundPadding']) - ($explnum_associate_cursor_svg_width / 2); $height = ($this->dimensions['speakerTop'] + count($this->speakers) * ($this->dimensions['speakerHeight'] + $this->dimensions['speakerMarginBottom'])) / $explnum_associate_cursor_svg_height; $transform = "translate(".$x.", 0) scale(1, ".$height.") translate(".(0 - $explnum_associate_cursor_svg_posX).", ".(0 - $explnum_associate_cursor_svg_posY).")"; $cursorSvg = str_replace("!!transform!!", $transform, $cursorSvg); $this->svg .= $cursorSvg; } /** * Retourne la chaine Javascript * @param boolean edit true pour activer la possibilité d'édition * @return string */ public function getJs($edit = false) { if ((!count($this->speakers)) || (!count($this->segments))) { $this->getDimensions(); $this->getDatas(); } // Calcul de la largeur totale disponible pour le ratio $availableWidth = $this->dimensions['totalWidth'] - ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + (2 * $this->dimensions['backgroundPadding'])); // Temps total $duration = $this->duration / 100; // Calcul ratio $ratio = $availableWidth / $duration; // Récupération des variables en js $this->js .= " var ratio = ".$ratio."; var player = videojs('videojs'); var segments = ".json_encode($this->segments).";"; // Déplacement du curseur $this->js .= " function update_cursor(){ document.getElementById('cursor_svg').transform.baseVal.getItem(0).setTranslate(".($this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + $this->dimensions['backgroundPadding'])." + (player.currentTime() * ratio), 0); document.getElementById('cursor_svg').setAttribute('title', get_time_to_display(player.currentTime())); } function get_time_to_display(tps){ tps = Math.round(tps); var sec = tps % 60; var min = (tps - sec) / 60; sec = '' + sec; while (sec.length < 2) { sec = '0' + sec; } return min + ':' + sec; } player.on('timeupdate', update_cursor); "; // Accès au début d'un segment en passant son id $this->js .= " function move_cursor_on_segment(id) { for (var i in segments) { if (segments[i].db_id == id) { player.currentTime(segments[i].start / 100); break; } } }"; // Drag du curseur $this->js .= " function start_drag_cursor(event) { event.preventDefault(); document.addEventListener('mouseup', stop_drag_cursor, false); document.addEventListener('mousemove', drag_cursor, false); } function drag_cursor(event) { update_video_time(event); } function stop_drag_cursor() { document.removeEventListener('mousemove', drag_cursor, false); document.removeEventListener('mouseup', stop_drag_cursor, false); } "; // Mise à jour de la vidéo $this->js .= " function update_video_time(event) { event.preventDefault(); player.currentTime((event.clientX - (findPos(document.getElementById('speech_timeline'))[0] + ". ($this->dimensions['speakerLeft'] + $this->dimensions['speakerWidth'] + $this->dimensions['speakerMarginRight'] + 2 * $this->dimensions['backgroundPadding']).")) / ratio); }"; // Ajout du listener sur le background $this->js .= " document.getElementById('background_svg').addEventListener('click', update_video_time, false);"; // Ajout du listener sur le curseur $this->js .= " document.getElementById('cursor_svg').addEventListener('mousedown', start_drag_cursor, false);"; if ($edit) { $this->getJsEdit(); } else { // Ajout du listener sur un segment $this->js .= " for (var i in segments) { document.getElementById('segment_svg_' + i).addEventListener('click', update_video_time, false); }"; } // Positionnement du curseur $this->js .= " update_cursor();"; return $this->js; } private function getJsEdit() { global $base_path; global $msg; // Récupération du tableau de locuteurs en js $this->js .= " var speakers = ".json_encode($this->speakers).";"; // Fonction d'ouverture du popup $this->js .= " function openPopUpCall(id) { openPopUp('./select.php?what=auteur&callback=update_associate_author&caller=explnum_associate_speaker_' + id + '¶m1=aut' + id + '_id¶m2=aut' + id + '&deb_rech='+encodeURIComponent(document.getElementById('aut' + id).value), 'selector'); }"; // Réinitialisation du formulaire $this->js .= " function clearAut(id) { document.getElementById('aut' + id).value=''; document.getElementById('aut' + id + '_id').value='0'; update_associate_author(); }"; // Validation du formulaire $this->js .= " function update_associate_author() { var id = document.getElementById('id_current_author_associate_form').value; if (document.getElementById('aut' + id).value != '') { document.getElementById('explnum_associate_author_libelle_' + id).innerHTML = document.getElementById('aut' + id).value; } else { document.getElementById('explnum_associate_author_libelle_' + id).innerHTML = '".$msg['explnum_associate_author']."'; } document.getElementById('author_associate_form_' + id).style.display = 'none'; var author_id = document.getElementById('aut' + id + '_id').value; var req = new http_request(); req.request('$base_path/ajax.php?module=catalog&categ=explnum&quoifaire=update_associate_author&speaker_id=' + id + '&author_id=' + author_id,0,'',1,'',''); }"; // Fermeture du formulaire $this->js .= " function close_author_associate_form(id) { document.getElementById('author_associate_form_' + id).style.display = 'none'; }"; // Création des div de selection d'autorité $this->js .= " for (var i in speakers) { if (!document.getElementById('author_associate_form_' + i)) { var form = document.createElement('form'); form.id = 'author_associate_form_' + i; form.className = 'form-catalog'; form.name = 'explnum_associate_speaker_' + i; var x = findPos(document.getElementById('speech_timeline'))[0] + ".$this->dimensions['speakerLeft']."; var y = findPos(document.getElementById('speech_timeline'))[1] - 10 + speakers[i]['posY']; form.style = 'position: absolute; top: ' + y + 'px; left: ' + x + 'px;'; form.addEventListener('submit', function(e){ e.preventDefault(); e.stopPropagation(); },false); var label = document.createElement('label'); label.className = 'etiquette'; label.for = 'aut' + i; label.innerHTML = '".$msg['234']."'; var img = document.createElement('img'); img.src = '".get_url_icon('close.png')."'; img.alt = '".$msg['197']."'; img.title = '".$msg['197']."'; img.className = 'right'; img.setAttribute('field_id', i); img.style.cursor = 'pointer'; img.addEventListener('click', function(){ close_author_associate_form(this.getAttribute('field_id')); }, false); var div = document.createElement('div'); div.className = 'row'; var span = document.createElement('span'); var input1 = document.createElement('input'); input1.type = 'text'; input1.id = 'aut' + i; input1.className = 'saisie-20emr'; input1.name = 'aut' + i; input1.setAttribute('autfield', 'aut' + i + '_id'); input1.setAttribute('completion', 'authors'); input1.setAttribute('autocompletion', 'on'); input1.value = speakers[i].author_libelle; input1.setAttribute('callback', 'update_associate_author'); var input2 = document.createElement('input'); input2.type = 'button'; input2.className = 'bouton'; input2.value = '...'; input2.setAttribute('field_id', i); input2.addEventListener('click', function(){ openPopUpCall(this.getAttribute('field_id')); }, false); var input3 = document.createElement('input'); input3.type = 'button'; input3.className = 'bouton'; input3.value = 'X'; input3.setAttribute('field_id', i); input3.addEventListener('click', function(){ clearAut(this.getAttribute('field_id')); }, false); var input4 = document.createElement('input'); input4.type = 'hidden'; input4.id = 'aut' + i + '_id'; input4.name = 'aut' + i + '_id'; input4.value = speakers[i].author; span.appendChild(input1); div.appendChild(span); div.appendChild(input2); div.appendChild(input3); div.appendChild(input4); form.appendChild(label); form.appendChild(img); form.appendChild(div); document.getElementById('att').appendChild(form); ajax_pack_element(document.getElementById('aut' + i)); document.getElementById('author_associate_form_' + i).style.display = 'none'; } } var input = document.createElement('input'); input.type = 'hidden'; input.id = 'id_current_author_associate_form'; input.value = 0; document.getElementById('att').appendChild(input); "; // Clic sur un auteur, on affiche le formulaire $this->js .= " function display_author_associate_form(event) { var current_id = document.getElementById('id_current_author_associate_form').value; if (current_id != 0) { document.getElementById('author_associate_form_' + current_id).style.display = 'none'; } var id = event.currentTarget.id.replace('explnum_associate_author_libelle_',''); document.getElementById('id_current_author_associate_form').value = id; document.getElementById('author_associate_form_' + id).style.display = 'block'; document.getElementById('aut' + id).focus(); } "; // Drag des segments $this->js .= " var current_drag_segment; var last_pageY; var current_drag_speaker_id; function set_current_speaker(id, is_current) { var current_drag_speaker = document.getElementById(id); if (is_current) { current_drag_speaker.setAttribute('stroke', 'red'); } else { current_drag_speaker.removeAttribute('stroke'); } } function start_drag_segment(event) { current_drag_segment = event.currentTarget; last_pageY = event.pageY; var segment_id = current_drag_segment.id.replace('segment_svg_',''); current_drag_speaker_id = 'speaker_svg_' + segments[segment_id]['speaker']; set_current_speaker(current_drag_speaker_id, true); document.addEventListener('mouseup', stop_drag_segment, false); document.addEventListener('mousemove', drag_segment, false); event.preventDefault(); var clone = current_drag_segment.cloneNode(true); clone.id = clone.id + '_clone'; clone.setAttribute('fill-opacity', 0.5); clone.setAttribute('stroke', 'red'); clone.setAttribute('stroke-dasharray','5,5'); document.getElementById('speech_timeline_svg').appendChild(clone); } function drag_segment(event) { var clone = document.getElementById(current_drag_segment.id + '_clone'); clone.transform.baseVal.getItem(0).setTranslate(clone.transform.baseVal.getItem(0).matrix.e, clone.transform.baseVal.getItem(0).matrix.f + event.pageY - last_pageY); last_pageY = event.pageY; var mouse_posY = event.pageY - findPos(document.getElementById('speech_timeline'))[1]; var speaker_id = current_drag_speaker_id.replace('speaker_svg_', ''); if ((speakers[speaker_id].posY > mouse_posY) || ((speakers[speaker_id].posY + ".$this->dimensions['speakerHeight'].") < mouse_posY)) { set_current_speaker(current_drag_speaker_id, false); for (var i in speakers) { if ((speakers[i].posY <= mouse_posY) && ((speakers[i].posY + ".$this->dimensions['speakerHeight'].") >= mouse_posY)) { current_drag_speaker_id = 'speaker_svg_' + i; set_current_speaker(current_drag_speaker_id, true); break; } } } } function stop_drag_segment(event) { document.removeEventListener('mousemove', drag_segment, false); document.removeEventListener('mouseup', stop_drag_segment, false); var clone = document.getElementById(current_drag_segment.id + '_clone'); clone.remove(); set_current_speaker(current_drag_speaker_id, false); var speaker_id = current_drag_speaker_id.replace('speaker_svg_', ''); var segment_id = current_drag_segment.id.replace('segment_svg_',''); if (segments[segment_id].speaker != speaker_id) { current_drag_segment.transform.baseVal.getItem(0).setTranslate(current_drag_segment.transform.baseVal.getItem(0).matrix.e, speakers[speaker_id].posY); segments[segment_id].speaker = speaker_id; var req = new http_request(); req.request('$base_path/ajax.php?module=catalog&categ=explnum&quoifaire=update_associate_speaker&segment_id=' + segments[segment_id].db_id + '&speaker_id=' + speaker_id,0,'',1,'',''); } } "; // Ajout d'un locuteur $this->js .= " function add_speaker(event) { var req = new http_request(); req.request('$base_path/ajax.php?module=catalog&categ=explnum&quoifaire=add_new_speaker&explnum_id=".$this->explnum_id."',0,'',1,get_explnum_associate_ajax,''); }"; // Suppression d'un locuteur $this->js .= " function del_speaker(event) { var speaker_id = event.currentTarget.id.replace('explnum_del_associate_speaker_', ''); hasSegment = false; for (var i in segments) { if (segments[i].speaker == speaker_id) { hasSegment = true; break; } } if (hasSegment) { alert('".$msg['explnum_del_associate_speaker_forbidden']."'); } else if (confirm('".$msg['explnum_del_associate_speaker_confirm']."')) { var req = new http_request(); req.request('$base_path/ajax.php?module=catalog&categ=explnum&quoifaire=delete_associate_speaker&speaker_id=' + speaker_id,0,'',1,get_explnum_associate_ajax,''); } }"; // Ajout du listener sur un segment $this->js .= " for (var i in segments) { document.getElementById('segment_svg_' + i).addEventListener('mousedown', start_drag_segment, false); }"; // Ajout des listeners sur les locuteurs (Ouverture formulaire et suppression) $this->js .= " for (var i in speakers) { document.getElementById('explnum_associate_author_libelle_' + i).addEventListener('click', display_author_associate_form, false); document.getElementById('explnum_del_associate_speaker_' + i).addEventListener('click', del_speaker, false); }"; // Ajout du listener sur le bouton d'ajout d'un locuteur $this->js .= " document.getElementById('explnum_associate_add_speaker').addEventListener('click', add_speaker, false);"; } } ?>