diff --git a/sites/all/modules/figli/edlp_studio/assets/js/edlp_studio.js b/sites/all/modules/figli/edlp_studio/assets/js/edlp_studio.js index 276c58786..8f979438d 100644 --- a/sites/all/modules/figli/edlp_studio/assets/js/edlp_studio.js +++ b/sites/all/modules/figli/edlp_studio/assets/js/edlp_studio.js @@ -3,6 +3,8 @@ var _settings = drupalSettings.edlp_studio; var $composer; + // audio play is handled by edlp_theme + function init(){ console.log('Studio Init'); initEvents(); @@ -21,11 +23,11 @@ initAjaxCompoLinks(); initDragAndDropUI(); } + // ___ _ _ _ _ _ _ _ _ _ // / __| |_ _ _| |_(_)___ _ _ | \| |___ __| |___ | | (_)_ _ | |__ ___ // | (__| ' \ || | _| / -_) '_| | .` / _ \/ _` / -_) | |__| | ' \| / /(_-< // \___|_||_\_,_|\__|_\___|_| |_|\_\___/\__,_\___| |____|_|_||_|_\_\/__/ - function initAjaxChutierLinks(){ console.log('studio initAjaxChutierLinks'); $('.chutier-ajax-link:not(.ajax-enabled)') @@ -119,6 +121,9 @@ $('.composition', $composer).replaceWith(data.compo); $composer.removeClass('ajax-loading'); initDragAndDropUI(); + $('body').trigger({ + 'type':'on-studio-compo-opened' + }); }; function onErrorOpenCompo(jqxhr, textStatus, error, $link){ $link.removeClass('ajax-loading'); @@ -172,12 +177,15 @@ }; function onCreateCompoDone(data, $link, $form){ console.log('onActionToCompoDone',data); + var $new_link = $(data.new_link); $link .removeClass('folded') .parents('li') - .before($('
  • ').append(data.new_link)); + .before($('
  • ').append($new_link)); $form.remove(); - // TODO: open new composition to composer + // open new composition to composer + initAjaxCompoLinks(); + openCompo($new_link); }; function onErrorCreateCompo(jqxhr, textStatus, error, $link){ console.warn('action to compo load failed : '+error, jqxhr.responseText); @@ -219,7 +227,6 @@ console.warn('delete compo load failed : '+error, jqxhr.responseText); }; - // ___ _ _ _ _ // / __| |_ _ _ __| (_)___ ___ _ _(_) // \__ \ _| || / _` | / _ \___| || | | @@ -253,7 +260,6 @@ } - // ___ __ ___ _ _ ___ // | \ _ _ __ _ __ _ / _|___ | \ _ _ ___ _ __ | | | |_ _| // | |) | '_/ _` / _` | > _|_ _| | |) | '_/ _ \ '_ \ | |_| || | @@ -337,9 +343,6 @@ }); }; - - // TODO: how to remove an item - function onOrderChanged(e, ui){ console.log('onOrderChanged : e', e); var cid = $(e.target).parents('.composition').attr('cid'); @@ -351,7 +354,6 @@ // console.log(documents); recordCompoList(cid, documents); }; - function recordCompoList(cid, docs){ console.log('recordCompoList '+cid, docs); var ajax_path = _settings.save_compo_ajax_url+'/'+cid; @@ -366,8 +368,10 @@ .fail(function(jqxhr, textStatus, error){ onErrorRecordCompo(jqxhr, textStatus, error); }); + $('body').trigger({ + 'type':'on-studio-compo-updated' + }); } - function onRecordCompoDone(data){ console.log('onDeleteCompoDone',data); if(data.status == "ok"){ @@ -379,7 +383,5 @@ console.warn('record compo failed : '+error, jqxhr.responseText); }; - - init(); })(jQuery, Drupal, drupalSettings); diff --git a/sites/all/modules/figli/edlp_studio/src/Controller/CompositionController.php b/sites/all/modules/figli/edlp_studio/src/Controller/CompositionController.php index 43d080f38..dd513731a 100644 --- a/sites/all/modules/figli/edlp_studio/src/Controller/CompositionController.php +++ b/sites/all/modules/figli/edlp_studio/src/Controller/CompositionController.php @@ -110,7 +110,7 @@ class CompositionController extends ControllerBase { '#options'=>array( 'attributes' => array( 'data-drupal-link-system-path' => $url->getInternalPath(), - 'nid' => $this->compo->id(), + 'cid' => $this->compo->id(), 'class' => ['composition-link'], 'title'=>$title, ), @@ -173,8 +173,10 @@ class CompositionController extends ControllerBase { $this->compo->setName($name); } $documents = array(); - foreach ($new_documents as $nid) { - $documents[] = ['target_id'=>$nid]; + if(count($new_documents)){ + foreach ($new_documents as $nid) { + $documents[] = ['target_id'=>$nid]; + } } $this->compo->set('documents', $documents); $this->compo->save(); diff --git a/sites/all/modules/figli/edlp_studio/src/Controller/StudioUIController.php b/sites/all/modules/figli/edlp_studio/src/Controller/StudioUIController.php index 55bb41249..f2ed8f3fd 100644 --- a/sites/all/modules/figli/edlp_studio/src/Controller/StudioUIController.php +++ b/sites/all/modules/figli/edlp_studio/src/Controller/StudioUIController.php @@ -119,7 +119,9 @@ class StudioUIController extends ControllerBase { '#composer_actions' => array( 'play_composition'=>array( "#type"=>'container', - "#markup"=>t('Play my composition'), + "#attributes"=>array( + "class"=>array("compo-player-controls") + ) ),// TODO: add social media links (what about composition visibility uotside studio_ui ?) ), ); diff --git a/sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js b/sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js index 4bc2b7aeb..d97417d42 100644 --- a/sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js +++ b/sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js @@ -6,8 +6,9 @@ var _$corpus_canvas; var _$row = $('main[role="main"]>.layout-content>.row'); var _$ajaxLinks; - var _audio_player; + var _audioPlayer; var _randomPlayer; + var _compoPlayer; // ___ _ _ // |_ _|_ _ (_) |_ @@ -20,7 +21,8 @@ _$body.on('corpus-map-ready', onCorpusMapReady); - _audio_player = new AudioPlayer(); + _audioPlayer = new AudioPlayer(); + _compoPlayer = new CompoPlayer(); initAjaxLinks(); @@ -42,271 +44,18 @@ // |___|\_/\___|_||_\__/__/ function initEvents(){ $('body') - .on('on-studio-chutier-updated', function(e){ + .on('on-studio-chutier-updated', initAjaxLinks) + .on('on-studio-compo-updated', function(e){ initAjaxLinks(); + _compoPlayer.refresh(); }) - .on('search-results-loaded',function(e){ + .on('on-studio-compo-opened', function(e){ initAjaxLinks(); + _compoPlayer.newCompo(); }) - .on('open_entree', function(e){ - // e.tid available - closeAllModals(); - }) - .on('close_entree', function(e){ - // e.tid available - backToFrontPage(); - }); - } - - // _ _ _ - // /_\ _ _ __| (_)___ - // / _ \ || / _` | / _ \ - // /_/ \_\_,_\__,_|_\___/ - // - // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement - // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/samples/gg589528%28v%3dvs.85%29 - // https://www.binarytides.com/using-html5-audio-element-javascript/ - function AudioPlayer(){ - var that = this; - this.fid; - this.audio = new Audio(); - // audio events - this.audio_events = ["loadedmetadata","canplay","playing","pause","timeupdate","ended"]; - - // UI dom objects - this.$container = $('
    '); - // btns - this.$btns = $('
    ').addClass('btns').appendTo(this.$container); - this.$previous = $('
    ').addClass('previous').appendTo(this.$btns); - this.$playpause = $('
    ').addClass('play-pause').appendTo(this.$btns); - this.$next = $('
    ').addClass('next').appendTo(this.$btns); - // timeline - this.$timelinecont= $('
    ').addClass('time-line-container').appendTo(this.$container); - this.$timeline = $('
    ').addClass('time-line').appendTo(this.$timelinecont); - this.$loader = $('
    ').addClass('loader').appendTo(this.$timeline); - this.$cursor = $('
    ').addClass('cursor').appendTo(this.$timeline); - // time - this.$time = $('
    ').addClass('time').appendTo(this.$container); - this.$currentTime = $('
    ').addClass('current-time').html('00:00').appendTo(this.$time); - this.$duration = $('
    ').addClass('duration').html('00:00').appendTo(this.$time); - // favoris - this.$fav = $('
    ').addClass('favoris').appendTo(this.$container); - // cartel - this.$cartel = $('
    ').addClass('cartel').appendTo(this.$container); - - // hiding - this.hideTimer = false; - this.hideTimeMS = 10000; - - // history - this.currentHistoricIndex = null; - this.historic = []; - this.shuffle_is_active = false; - - // object events - this.event_handlers = { - 'audio-play-next':[], - 'audio-ended':[], - 'stop-shuffle':[] - }; - - this.init(); - }; - AudioPlayer.prototype = { - init(){ - // append ui to document - this.$container.appendTo('header[role="banner"] .region-header'); - // record timeline width - this.timeline_w = parseInt(this.$timeline.width()); - // init audio events - var fn = ''; - for (var i = 0; i < this.audio_events.length; i++) { - fn = this.audio_events[i]; - // capitalize first letter of event (only cosmetic :p ) - fn = 'on'+fn.charAt(0).toUpperCase()+fn.slice(1); - this.audio.addEventListener( - this.audio_events[i], - this[fn].bind(this), - true); - } - // init btns events - this.$previous.on('click', this.playPrevious.bind(this)); - this.$playpause.on('click', this.togglePlayPause.bind(this)); - this.$next.on('click', this.playNext.bind(this)); - // TODO: previous and next btns - }, - openDocument(node){ - console.log('AudioPlayer openDocument', node); - this.historic.push(node); - this.currentHistoricIndex = this.historic.length-1; - // this.shuffle_mode = shuffle_mode || false; - this.launch(); - }, - launch(){ - this.clearTimeOutToHide(); - this.setSRC(this.historic[this.currentHistoricIndex].audio_url); - this.loadNode(this.historic[this.currentHistoricIndex].nid); - // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer) - try { - _$corpus_canvas.trigger({ - 'type':'audio-node-opened', - 'nid':this.historic[this.currentHistoricIndex].nid - }); - } catch (e) { - console.warn('AudioPlayer : _$corpus_canvas does not exists'); - } - - this.showHidePreviousBtn(); - this.showHideNextBtn(); - this.show(); - }, - // audio functions - setSRC(url){ - // console.log('AudioPlayer setSRC : url', url); - this.audio.src = url; - }, - onLoadedmetadata(){ - var rem = parseInt(this.audio.duration, 10), - mins = Math.floor(rem/60,10), - secs = rem - mins*60; - this.$duration.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); - this.updateLoadingBar(); - }, - updateLoadingBar(){ - this.$loader.css({ - 'width':parseInt((100 * this.audio.buffered.end(0) / this.audio.duration), 10)+'%' - }); - if( this.audio.buffered.end(0) < this.audio.duration ){ - // loop through this function until file is fully loaded - var that = this; - window.requestAnimationFrame(that.updateLoadingBar.bind(that)); - }else{ - console.log('Audio fully loaded'); - } - }, - onCanplay(){ - this.play(); - }, - play(){ - this.audio.play(); - }, - playPrevious(){ - if(this.currentHistoricIndex > 0){ - this.currentHistoricIndex -= 1; - this.launch(); - } - }, - playNext(){ - if(this.currentHistoricIndex < this.historic.length-1){ - this.currentHistoricIndex += 1; - this.launch(); - }else{ - this.emmit('audio-play-next'); - } - }, - togglePlayPause(e){ - if(this.audio.paused){ - this.audio.play(); - }else{ - this.audio.pause(); - } - }, - stop(){ - console.log('AudioPlayer stop()'); - this.audio.pause(); - this.timeOutToHide(); - }, - // audio events - onPlaying(){ - this.$container.addClass('is-playing'); - }, - onPause(){ - this.$container.removeClass('is-playing'); - }, - onTimeupdate(){ - // move cursor - this.$cursor.css({ - 'left':(this.audio.currentTime/this.audio.duration * this.timeline_w)+"px" - }); - // update time text display - var rem = parseInt(this.audio.currentTime, 10), - mins = Math.floor(rem/60,10), - secs = rem - mins*60; - this.$currentTime.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); - }, - onEnded(){ - this.emmit('audio-ended'); - this.stop(); - }, - // cartel functions - loadNode(nid){ - this.$cartel.addClass('loading'); - $.getJSON('/edlp/ajax/json/node/'+nid+'/player_cartel', {}) - .done(this.onNodeLoaded.bind(this)) - .fail(this.onNodeLoadFail.bind(this)); - }, - onNodeLoaded(data){ - console.log('AudioPlayer node loaded'); - this.$cartel.html(data.rendered).removeClass('loading'); - _$body.trigger({'type':'new-audio-cartel-loaded'}); - initAjaxLinks(); - }, - onNodeLoadFail(jqxhr, textStatus, error){ - console.warn('AudioPlayer node load failed', jqxhr.responseText); - this.$cartel.removeClass('loading').html(''); - }, - // global - show(){ - this.$container.addClass('visible'); - }, - showHidePreviousBtn(){ - if(this.historic.length > 1 && this.currentHistoricIndex > 0){ - this.$previous.addClass('is-active'); - }else{ - this.$previous.removeClass('is-active'); - } - }, - showHideNextBtn(){ - if(this.currentHistoricIndex < this.historic.length-1 || this.shuffle_is_active){ - this.$next.addClass('is-active'); - }else{ - this.$next.removeClass('is-active'); - } - }, - timeOutToHide(){ - console.log('AudioPlayer timeOutToHide()'); - this.clearTimeOutToHide(); - this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS); - }, - clearTimeOutToHide(){ - console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer); - if(this.hideTimer){ - clearTimeout(this.hideTimer); - this.hideTimer = false; - } - }, - hide(){ - console.log('AudioPlayer hide()'); - this.$container.removeClass('visible'); - // trigger highlighted node remove on corpus map - _$corpus_canvas.trigger('audio-node-closed'); - }, - // object events - on(event_name, handler){ - if(typeof this.event_handlers[event_name] == 'undefined'){ - console.warn('AudioPlayer : event '+event_name+' does not exists'); - } - this.event_handlers[event_name].push(handler); - return this; - }, - emmit(event_name){ - var handler; - for (var i = this.event_handlers[event_name].length; i >= 0 ; i--) { - handler = this.event_handlers[event_name][i]; - setTimeout(handler, 0); - } - return this; - }, + .on('search-results-loaded', initAjaxLinks) + .on('open_entree', closeAllModals) + .on('close_entree', backToFrontPage); } // ___ _ _ ___ @@ -365,7 +114,7 @@ // Audio links if($link.is('.audio-link')){ // TODO: stop randomplayer - _audio_player + _audioPlayer .emmit('stop-shuffle') .openDocument({ nid:$link.attr('nid'), @@ -521,7 +270,7 @@ }) .on('corpus-cliked-on-node', function(e) { console.log('theme : corpus-cliked-on-node', e); - _audio_player + _audioPlayer .emmit('stop-shuffle') .openDocument(e.target_node); }); @@ -531,6 +280,284 @@ _$body.attr('corpus-map', 'ready'); } + + // _ _ _ + // /_\ _ _ __| (_)___ + // / _ \ || / _` | / _ \ + // /_/ \_\_,_\__,_|_\___/ + // + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement + // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/samples/gg589528%28v%3dvs.85%29 + // https://www.binarytides.com/using-html5-audio-element-javascript/ + function AudioPlayer(){ + var that = this; + this.fid; + this.audio = new Audio(); + // audio events + this.audio_events = ["loadedmetadata","canplay","playing","pause","timeupdate","ended"]; + + // UI dom objects + this.$container = $('
    '); + // btns + this.$btns = $('
    ').addClass('btns').appendTo(this.$container); + this.$previous = $('
    ').addClass('previous').appendTo(this.$btns); + this.$playpause = $('
    ').addClass('play-pause').appendTo(this.$btns); + this.$next = $('
    ').addClass('next').appendTo(this.$btns); + // timeline + this.$timelinecont= $('
    ').addClass('time-line-container').appendTo(this.$container); + this.$timeline = $('
    ').addClass('time-line').appendTo(this.$timelinecont); + this.$loader = $('
    ').addClass('loader').appendTo(this.$timeline); + this.$cursor = $('
    ').addClass('cursor').appendTo(this.$timeline); + // time + this.$time = $('
    ').addClass('time').appendTo(this.$container); + this.$currentTime = $('
    ').addClass('current-time').html('00:00').appendTo(this.$time); + this.$duration = $('
    ').addClass('duration').html('00:00').appendTo(this.$time); + // favoris + this.$fav = $('
    ').addClass('favoris').appendTo(this.$container); + // cartel + this.$cartel = $('
    ').addClass('cartel').appendTo(this.$container); + + // hiding + this.hideTimer = false; + this.hideTimeMS = 10000; + + // history + this.currentHistoricIndex = null; + this.historic = []; + this.shuffle_is_active = false; + + // object events + this.event_handlers = { + 'audio-open-document':[], + 'audio-play':[], + 'audio-pause':[], + 'audio-play-next':[], + 'audio-ended':[], + 'stop-shuffle':[] + }; + + this.init(); + }; + AudioPlayer.prototype = { + init(){ + // append ui to document + this.$container.appendTo('header[role="banner"] .region-header'); + // record timeline width + this.timeline_w = parseInt(this.$timeline.width()); + // init audio events + var fn = ''; + for (var i = 0; i < this.audio_events.length; i++) { + fn = this.audio_events[i]; + // capitalize first letter of event (only cosmetic :p ) + fn = 'on'+fn.charAt(0).toUpperCase()+fn.slice(1); + this.audio.addEventListener( + this.audio_events[i], + this[fn].bind(this), + true); + } + // init btns events + this.$previous.on('click', this.playPrevious.bind(this)); + this.$playpause.on('click', this.togglePlayPause.bind(this)); + this.$next.on('click', this.playNext.bind(this)); + // TODO: previous and next btns + }, + openDocument(node, caller){ + // console.log('AudioPlayer openDocument', node); + if(typeof node == 'undefined' + || typeof node.nid == 'undefined' + || typeof node.audio_url == 'undfined'){ + console.warn('AudioPlayer openDocument() node is malformed', node); + return false; + } + this.historic.push(node); + this.currentHistoricIndex = this.historic.length-1; + // this.shuffle_mode = shuffle_mode || false; + + this.emmit('audio-open-document', {caller:caller}); + + this.launch(); + }, + launch(){ + this.clearTimeOutToHide(); + this.setSRC(this.historic[this.currentHistoricIndex].audio_url); + this.loadNode(this.historic[this.currentHistoricIndex].nid); + // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer) + try { + _$corpus_canvas.trigger({ + 'type':'audio-node-opened', + 'nid':this.historic[this.currentHistoricIndex].nid + }); + } catch (e) { + console.info('AudioPlayer : _$corpus_canvas does not exists'); + } + + this.showHidePreviousBtn(); + this.showHideNextBtn(); + this.show(); + }, + // audio functions + setSRC(url){ + // console.log('AudioPlayer setSRC : url', url); + this.audio.src = url; + }, + onLoadedmetadata(){ + var rem = parseInt(this.audio.duration, 10), + mins = Math.floor(rem/60,10), + secs = rem - mins*60; + this.$duration.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); + this.updateLoadingBar(); + }, + updateLoadingBar(){ + this.$loader.css({ + 'width':parseInt((100 * this.audio.buffered.end(0) / this.audio.duration), 10)+'%' + }); + if( this.audio.buffered.end(0) < this.audio.duration ){ + // loop through this function until file is fully loaded + var that = this; + window.requestAnimationFrame(that.updateLoadingBar.bind(that)); + }else{ + console.log('Audio fully loaded'); + } + }, + onCanplay(){ + this.play(); + }, + play(){ + this.clearTimeOutToHide(); + this.audio.play(); + }, + playPrevious(){ + if(this.currentHistoricIndex > 0){ + this.currentHistoricIndex -= 1; + this.launch(); + } + }, + playNext(){ + if(this.currentHistoricIndex < this.historic.length-1){ + this.currentHistoricIndex += 1; + this.launch(); + }else{ + this.emmit('audio-play-next'); + } + }, + togglePlayPause(e){ + if(this.audio.paused){ + this.audio.play(); + }else{ + this.audio.pause(); + } + }, + stop(){ + // console.log('AudioPlayer stop()'); + this.audio.pause(); + this.timeOutToHide(); + }, + // audio events + onPlaying(){ + this.$btns.addClass('is-playing'); + this.emmit('audio-play'); + }, + onPause(){ + this.$btns.removeClass('is-playing'); + this.emmit('audio-pause'); + }, + onTimeupdate(){ + // move cursor + this.$cursor.css({ + 'left':(this.audio.currentTime/this.audio.duration * this.timeline_w)+"px" + }); + // update time text display + var rem = parseInt(this.audio.currentTime, 10), + mins = Math.floor(rem/60,10), + secs = rem - mins*60; + this.$currentTime.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); + }, + onEnded(){ + this.emmit('audio-ended'); + this.stop(); + }, + // cartel functions + loadNode(nid){ + this.$cartel.addClass('loading'); + $.getJSON('/edlp/ajax/json/node/'+nid+'/player_cartel', {}) + .done(this.onNodeLoaded.bind(this)) + .fail(this.onNodeLoadFail.bind(this)); + }, + onNodeLoaded(data){ + // console.log('AudioPlayer node loaded'); + this.$cartel.html(data.rendered).removeClass('loading'); + _$body.trigger({'type':'new-audio-cartel-loaded'}); + initAjaxLinks(); + }, + onNodeLoadFail(jqxhr, textStatus, error){ + console.warn('AudioPlayer node load failed', jqxhr.responseText); + this.$cartel.removeClass('loading').html(''); + }, + // global + show(){ + this.$container.addClass('visible'); + }, + showHidePreviousBtn(){ + if(this.historic.length > 1 && this.currentHistoricIndex > 0){ + this.$previous.addClass('is-active'); + }else{ + this.$previous.removeClass('is-active'); + } + }, + showHideNextBtn(){ + if(this.currentHistoricIndex < this.historic.length-1 || this.shuffle_is_active){ + this.$next.addClass('is-active'); + }else{ + this.$next.removeClass('is-active'); + } + }, + timeOutToHide(){ + // console.log('AudioPlayer timeOutToHide()'); + this.clearTimeOutToHide(); + this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS); + }, + clearTimeOutToHide(){ + // console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer); + if(this.hideTimer){ + clearTimeout(this.hideTimer); + this.hideTimer = false; + } + }, + hide(){ + // console.log('AudioPlayer hide()'); + this.$container.removeClass('visible'); + // trigger highlighted node remove on corpus map + try { + _$corpus_canvas.trigger('audio-node-closed'); + } catch (e) { + console.info('AudioPlayer hide() : _$corpus_canvas does not exists'); + } + }, + // object events + on(event_name, handler){ + if(typeof this.event_handlers[event_name] == 'undefined'){ + console.warn('AudioPlayer : event '+event_name+' does not exists'); + } + this.event_handlers[event_name].push(handler); + return this; + }, + emmit(event_name, args){ + // console.log('AudioPlayer emmit() event_name', event_name); + // console.log('AudioPlayer emmit() handlers', this.event_handlers[event_name]); + var handler; + var args = args || {}; + for (var i = this.event_handlers[event_name].length-1; i >= 0 ; i--) { + handler = this.event_handlers[event_name][i]; + // console.log('AudioPlayer emmit() loop handler', handler); + setTimeout(function(){ + // console.log('AudioPlayer emmit() timeout handler', handler); + handler(args); + }, 0); + } + return this; + }, + } + // ___ _ ___ _ // | _ \__ _ _ _ __| |___ _ __ | _ \ |__ _ _ _ ___ _ _ // | / _` | ' \/ _` / _ \ ' \| _/ / _` | || / -_) '_| @@ -540,7 +567,7 @@ this.active = false; this.playlist = playlist; this.$btn = $('').html('Shuffle').addClass('random-player-btn'); - this.init() + this.init(); }; RandomPlayer.prototype = { init(){ @@ -554,7 +581,7 @@ this.$btn.on('click', this.toggleActive.bind(this)); // attach an event on AudioPlayer - _audio_player + _audioPlayer .on('audio-ended', this.onAudioPlayerEnded.bind(this)) .on('audio-play-next', this.onAudioPlayNext.bind(this)) .on('stop-shuffle', this.stop.bind(this)); @@ -579,20 +606,20 @@ } }, start(){ - this.active = _audio_player.shuffle_is_active = true; + this.active = _audioPlayer.shuffle_is_active = true; this.$btn.addClass('is-active'); this.shuffle(); this.next(); }, stop(){ - this.active = _audio_player.shuffle_is_active = false; + this.active = _audioPlayer.shuffle_is_active = false; this.$btn.removeClass('is-active'); // stop audio player - // _audio_player.stop(); + // _audioPlayer.stop(); }, next(){ if(this.active && this.shuffledPlaylist.length > 0) - _audio_player.openDocument(this.shuffledPlaylist.splice(0,1)[0]); + _audioPlayer.openDocument(this.shuffledPlaylist.splice(0,1)[0]); }, onAudioPlayNext(){ console.log('RandomPlayer : onAudioPlayNext()'); @@ -604,6 +631,215 @@ } }; + // ___ ___ _ + // / __|___ _ __ _ __ ___| _ \ |__ _ _ _ ___ _ _ + // | (__/ _ \ ' \| '_ \/ _ \ _/ / _` | || / -_) '_| + // \___\___/_|_|_| .__/\___/_| |_\__,_|\_, \___|_| + // |_| |__/ + function CompoPlayer(){ + this.playing = false; + this.paused = false; + this.playlist = []; + this.current_index = 0; + this.$compo = null; + this.init(); + }; + CompoPlayer.prototype = { + init(){ + console.log('CompoPlayer init()'); + // attach an event on AudioPlayer + _audioPlayer + .on('audio-open-document', this.onAudioOpenDocument.bind(this)) + .on('audio-play', this.onAudioPlayerPlay.bind(this)) + .on('audio-pause', this.onAudioPlayerPause.bind(this)) + .on('audio-ended', this.onAudioPlayerEnded.bind(this)); + // .on('audio-play-next', this.onAudioPlayNext.bind(this)); + + this.newCompo(); + }, + newCompo(){ + console.log('CompoPlayer newCompo()'); + // $('.composition_ui .composer .composition .field--name-documents') + this.$composer = $('.composition_ui .composer'); + this.$compo = $('.composition_ui .composer .composition'); + this.$controls = $('.composition_ui .composer .compo-player-controls'); + this.$previous = $('
    ').addClass('previous') + .on('click', this.prev.bind(this)) + .appendTo(this.$controls); + this.$playpause = $('
    ').addClass('play-pause') + .on('click', this.togglePlayPause.bind(this)) + .appendTo(this.$controls); + this.$next = $('
    ').addClass('next') + .on('click', this.next.bind(this)) + .appendTo(this.$controls); + // this.$playpause = $('.compo-player-controls', this.$composer) + // .on('click', this.togglePlayPause.bind(this)); + this.refresh(); + }, + refresh(){ + // console.log('CompoPlayer refresh(), this', this); + // this.playing = false; + // this.paused = false; + // this.resetIndex(); + this.stop(); + + // load new playlist + this.playlist = []; + var that = this; + $('.field--name-documents .field__item',this.$compo).each(function(i,el){ + var $link = $('a.audio-link',this); + that.playlist.push({ + item:$(this), + audio_url:$link.attr("audio_url"), + nid:$link.attr("nid"), + }); + }); + this.showHideControls(); + }, + togglePlayPause(){ + // console.log('CompoPlayer togglePlayPause'); + if (this.playing && !this.paused) { + this.pause(); + }else{ + if(this.playing && this.paused){ + this.play(); + }else{ + this.start(); + } + } + }, + start(){ + console.log('start'); + // console.log('CompoPlayer start()'); + this.playing = true; + this.play(); + }, + play(){ + // console.log('play'); + if(this.paused){ + this.paused = false; + _audioPlayer.play(); + }else{ + _audioPlayer.openDocument(this.playlist[this.current_index], this); + } + this.setActiveItem().showHideControls(); + }, + pause(){ + console.log('pause'); + this.paused = true; + this.showHideControls(); + _audioPlayer.stop(); + }, + next(){ + // console.log('CompoPlayer next()'); + if(this.playing){ + this.current_index += 1; + if(this.current_index < this.playlist.length){ + this.play(); + }else{ + this.stop(); + } + } + }, + prev(){ + // console.log('CompoPlayer prev()'); + if(this.playing){ + this.current_index -= 1; + if(this.current_index >= 0){ + this.play(); + }else{ + this.stop(); + } + } + }, + stop(){ + _audioPlayer.stop(); + this.reset(); + }, + reset(){ + this.playing = false; + this.paused = false; + this.resetIndex(); + }, + resetIndex(){ + this.current_index = 0; + this.showHideControls().resetActiveItems(); + }, + setActiveItem(){ + this.resetActiveItems(); + if(this.playing && this.current_index >= 0){ + this.playlist[this.current_index].item.addClass('is-active'); + } + // this call shoud not be here + this.showHideControls(); + return this; + }, + resetActiveItems(){ + for (var n = 0; n < this.playlist.length; n++) { + // console.log('node',node); + this.playlist[n].item.removeClass('is-active'); + } + return this; + }, + showHideControls(){ + // console.log('CompoPlayer showHideNextBtn(), playing:'+this.playing+', paused:'+this.paused); + // global playing + if(this.playing && !this.paused){ + this.$controls.addClass('is-playing'); + }else{ + this.$controls.removeClass('is-playing'); + } + // playpause + if(this.playlist.length > 0){ + this.$playpause.addClass('is-active'); + }else{ + this.$playpause.removeClass('is-active'); + } + // next + if(this.playing && this.playlist.length > 1 && this.current_index < this.playlist.length -1){ + this.$next.addClass('is-active'); + }else{ + this.$next.removeClass('is-active'); + } + // previous + if(this.playing && this.playlist.length > 1 && this.current_index > 0){ + this.$previous.addClass('is-active'); + }else{ + this.$previous.removeClass('is-active'); + } + return this; + }, + // _audioPlayer events + onAudioOpenDocument(args){ + if(args.caller !== this){ + // console.log('CompoPlayer onAudioOpenDocument() called by other'); + this.reset(); + } + // else{ + // // console.log('CompoPlayer onAudioOpenDocument() self calling'); + // } + }, + onAudioPlayerPlay(){ + if(this.playing && this.paused){ + this.paused = false; + this.showHideControls(); + } + }, + onAudioPlayerPause(){ + if(this.playing && !this.paused){ + this.paused = true; + this.showHideControls(); + } + }, + onAudioPlayerEnded(){ + this.next(); + }, + // onAudioPlayNext(){ + // this.next(); + // } + }; + + // ___ _ _ _ // | _ \_ _ ___ __| |_ _ __| |_(_)___ _ _ ___ // | _/ '_/ _ \/ _` | || / _| _| / _ \ ' \(_-< diff --git a/sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css b/sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css index 7267fff42..56ab2e500 100644 --- a/sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css +++ b/sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css @@ -1371,6 +1371,8 @@ main[role="main"] span.close-col-btn { background-image: url(../img/audio-player-play.svg); padding: 0 0.3em; cursor: pointer; } + #audio-player .btns.is-playing .play-pause { + background-image: url(../img/audio-player-pause.svg); } #audio-player .btns .previous, #audio-player .btns .next { opacity: 0.3; -webkit-transition: opacity 0.3s ease-in-out; @@ -1506,8 +1508,6 @@ main[role="main"] span.close-col-btn { font-size: 0.75em; } #audio-player .cartel:hover .second-cartel { opacity: 1; } - #audio-player.is-playing .btns .play-pause { - background-image: url(../img/audio-player-pause.svg); } .chutier-icon { z-index: 1; @@ -1697,6 +1697,17 @@ main[role="main"] span.close-col-btn { display: inline-block; vertical-align: middle; margin-right: 5px; } + #studio-ui .composition_ui > .wrapper .compositions-list a.composition-link.ajax-loading:before { + -webkit-animation: rotation 2s infinite linear; + animation: rotation 2s infinite linear; } + +@keyframes rotation { + from { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); } + to { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); } } #studio-ui .composition_ui > .wrapper .compositions-list a.composition-link.is-active:before { background-color: black; } #studio-ui .composition_ui > .wrapper .compositions-list a.delete-composition-link { @@ -1751,13 +1762,19 @@ main[role="main"] span.close-col-btn { -webkit-box-sizing: border-box; box-sizing: border-box; padding-left: 0.5em; - width: 69%; } + width: 69%; + opacity: 1; + -webkit-transition: opacity 0.3s ease-in-out; + transition: opacity 0.3s ease-in-out; } + #studio-ui .composition_ui > .wrapper .composer.ajax-loading { + opacity: 0.3; } #studio-ui .composition_ui > .wrapper .composer > * { -webkit-box-sizing: border-box; box-sizing: border-box; } #studio-ui .composition_ui > .wrapper .composer > header { height: 25px; - padding-bottom: 5px; } + padding-bottom: 5px; + font-size: 0.756em; } #studio-ui .composition_ui > .wrapper .composer > .composition { position: relative; height: calc(100% - 60px); @@ -1785,10 +1802,10 @@ main[role="main"] span.close-col-btn { #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item { display: inline-block; position: relative; - width: 25px; - height: 100%; white-space: nowrap; pointer-events: none; + width: 25px; + height: 100%; opacity: 1; -webkit-transition: opacity 0.1s ease-in-out; transition: opacity 0.1s ease-in-out; } @@ -1810,6 +1827,7 @@ main[role="main"] span.close-col-btn { margin: 0; text-transform: none; } #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item article h2 a { + white-space: nowrap !important; font-size: 0.756em; } #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item article h2 a:before { content: ""; @@ -1835,6 +1853,8 @@ main[role="main"] span.close-col-btn { transform: rotateZ(-45deg); border: 1px solid red; background-color: white; } + #studio-ui .composition_ui > .wrapper .composer > .composition .field--name-documents .field__item.is-active .handler { + background-color: red; } #studio-ui .composition_ui > .wrapper .composer > .composition .remove-drop-zone { position: absolute; top: 0; @@ -1845,13 +1865,38 @@ main[role="main"] span.close-col-btn { #studio-ui .composition_ui > .wrapper .composer > .actions { padding-top: 5px; height: 25px; } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls > * { + display: inline-block; + vertical-align: middle; + width: 20px; + height: 30px; + background-position: center; + background-size: contain; } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .play-pause { + background-image: url(../img/audio-player-play.svg); + padding: 0 0.3em; + cursor: pointer; } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls.is-playing .play-pause { + background-image: url(../img/audio-player-pause.svg); } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .previous, #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .next { + opacity: 0.3; + -webkit-transition: opacity 0.3s ease-in-out; + transition: opacity 0.3s ease-in-out; + cursor: auto; } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .previous.is-active, #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .next.is-active { + opacity: 1; + cursor: pointer; } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .previous { + background-image: url(../img/audio-player-previous.svg); } + #studio-ui .composition_ui > .wrapper .composer > .actions .compo-player-controls .next { + background-image: url(../img/audio-player-next.svg); } #studio-ui .field__item.ui-draggable-dragging { display: inline-block; position: relative; - width: 25px; - height: 100%; white-space: nowrap; pointer-events: none; + width: 25px; + height: 100%; opacity: 1; -webkit-transition: opacity 0.1s ease-in-out; transition: opacity 0.1s ease-in-out; } @@ -1873,6 +1918,7 @@ main[role="main"] span.close-col-btn { margin: 0; text-transform: none; } #studio-ui .field__item.ui-draggable-dragging article h2 a { + white-space: nowrap !important; font-size: 0.756em; } #studio-ui .field__item.ui-draggable-dragging article h2 a:before { content: ""; @@ -1898,6 +1944,8 @@ main[role="main"] span.close-col-btn { transform: rotateZ(-45deg); border: 1px solid red; background-color: white; } + #studio-ui .field__item.ui-draggable-dragging.is-active .handler { + background-color: red; } .col[theme="edlp_search_search_form"], .col[theme="edlp_search_search_form"] > .wrapper { position: relative; diff --git a/sites/all/themes/custom/edlptheme/assets/scripts/main.js b/sites/all/themes/custom/edlptheme/assets/scripts/main.js index 4bc2b7aeb..d97417d42 100644 --- a/sites/all/themes/custom/edlptheme/assets/scripts/main.js +++ b/sites/all/themes/custom/edlptheme/assets/scripts/main.js @@ -6,8 +6,9 @@ var _$corpus_canvas; var _$row = $('main[role="main"]>.layout-content>.row'); var _$ajaxLinks; - var _audio_player; + var _audioPlayer; var _randomPlayer; + var _compoPlayer; // ___ _ _ // |_ _|_ _ (_) |_ @@ -20,7 +21,8 @@ _$body.on('corpus-map-ready', onCorpusMapReady); - _audio_player = new AudioPlayer(); + _audioPlayer = new AudioPlayer(); + _compoPlayer = new CompoPlayer(); initAjaxLinks(); @@ -42,271 +44,18 @@ // |___|\_/\___|_||_\__/__/ function initEvents(){ $('body') - .on('on-studio-chutier-updated', function(e){ + .on('on-studio-chutier-updated', initAjaxLinks) + .on('on-studio-compo-updated', function(e){ initAjaxLinks(); + _compoPlayer.refresh(); }) - .on('search-results-loaded',function(e){ + .on('on-studio-compo-opened', function(e){ initAjaxLinks(); + _compoPlayer.newCompo(); }) - .on('open_entree', function(e){ - // e.tid available - closeAllModals(); - }) - .on('close_entree', function(e){ - // e.tid available - backToFrontPage(); - }); - } - - // _ _ _ - // /_\ _ _ __| (_)___ - // / _ \ || / _` | / _ \ - // /_/ \_\_,_\__,_|_\___/ - // - // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement - // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/samples/gg589528%28v%3dvs.85%29 - // https://www.binarytides.com/using-html5-audio-element-javascript/ - function AudioPlayer(){ - var that = this; - this.fid; - this.audio = new Audio(); - // audio events - this.audio_events = ["loadedmetadata","canplay","playing","pause","timeupdate","ended"]; - - // UI dom objects - this.$container = $('
    '); - // btns - this.$btns = $('
    ').addClass('btns').appendTo(this.$container); - this.$previous = $('
    ').addClass('previous').appendTo(this.$btns); - this.$playpause = $('
    ').addClass('play-pause').appendTo(this.$btns); - this.$next = $('
    ').addClass('next').appendTo(this.$btns); - // timeline - this.$timelinecont= $('
    ').addClass('time-line-container').appendTo(this.$container); - this.$timeline = $('
    ').addClass('time-line').appendTo(this.$timelinecont); - this.$loader = $('
    ').addClass('loader').appendTo(this.$timeline); - this.$cursor = $('
    ').addClass('cursor').appendTo(this.$timeline); - // time - this.$time = $('
    ').addClass('time').appendTo(this.$container); - this.$currentTime = $('
    ').addClass('current-time').html('00:00').appendTo(this.$time); - this.$duration = $('
    ').addClass('duration').html('00:00').appendTo(this.$time); - // favoris - this.$fav = $('
    ').addClass('favoris').appendTo(this.$container); - // cartel - this.$cartel = $('
    ').addClass('cartel').appendTo(this.$container); - - // hiding - this.hideTimer = false; - this.hideTimeMS = 10000; - - // history - this.currentHistoricIndex = null; - this.historic = []; - this.shuffle_is_active = false; - - // object events - this.event_handlers = { - 'audio-play-next':[], - 'audio-ended':[], - 'stop-shuffle':[] - }; - - this.init(); - }; - AudioPlayer.prototype = { - init(){ - // append ui to document - this.$container.appendTo('header[role="banner"] .region-header'); - // record timeline width - this.timeline_w = parseInt(this.$timeline.width()); - // init audio events - var fn = ''; - for (var i = 0; i < this.audio_events.length; i++) { - fn = this.audio_events[i]; - // capitalize first letter of event (only cosmetic :p ) - fn = 'on'+fn.charAt(0).toUpperCase()+fn.slice(1); - this.audio.addEventListener( - this.audio_events[i], - this[fn].bind(this), - true); - } - // init btns events - this.$previous.on('click', this.playPrevious.bind(this)); - this.$playpause.on('click', this.togglePlayPause.bind(this)); - this.$next.on('click', this.playNext.bind(this)); - // TODO: previous and next btns - }, - openDocument(node){ - console.log('AudioPlayer openDocument', node); - this.historic.push(node); - this.currentHistoricIndex = this.historic.length-1; - // this.shuffle_mode = shuffle_mode || false; - this.launch(); - }, - launch(){ - this.clearTimeOutToHide(); - this.setSRC(this.historic[this.currentHistoricIndex].audio_url); - this.loadNode(this.historic[this.currentHistoricIndex].nid); - // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer) - try { - _$corpus_canvas.trigger({ - 'type':'audio-node-opened', - 'nid':this.historic[this.currentHistoricIndex].nid - }); - } catch (e) { - console.warn('AudioPlayer : _$corpus_canvas does not exists'); - } - - this.showHidePreviousBtn(); - this.showHideNextBtn(); - this.show(); - }, - // audio functions - setSRC(url){ - // console.log('AudioPlayer setSRC : url', url); - this.audio.src = url; - }, - onLoadedmetadata(){ - var rem = parseInt(this.audio.duration, 10), - mins = Math.floor(rem/60,10), - secs = rem - mins*60; - this.$duration.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); - this.updateLoadingBar(); - }, - updateLoadingBar(){ - this.$loader.css({ - 'width':parseInt((100 * this.audio.buffered.end(0) / this.audio.duration), 10)+'%' - }); - if( this.audio.buffered.end(0) < this.audio.duration ){ - // loop through this function until file is fully loaded - var that = this; - window.requestAnimationFrame(that.updateLoadingBar.bind(that)); - }else{ - console.log('Audio fully loaded'); - } - }, - onCanplay(){ - this.play(); - }, - play(){ - this.audio.play(); - }, - playPrevious(){ - if(this.currentHistoricIndex > 0){ - this.currentHistoricIndex -= 1; - this.launch(); - } - }, - playNext(){ - if(this.currentHistoricIndex < this.historic.length-1){ - this.currentHistoricIndex += 1; - this.launch(); - }else{ - this.emmit('audio-play-next'); - } - }, - togglePlayPause(e){ - if(this.audio.paused){ - this.audio.play(); - }else{ - this.audio.pause(); - } - }, - stop(){ - console.log('AudioPlayer stop()'); - this.audio.pause(); - this.timeOutToHide(); - }, - // audio events - onPlaying(){ - this.$container.addClass('is-playing'); - }, - onPause(){ - this.$container.removeClass('is-playing'); - }, - onTimeupdate(){ - // move cursor - this.$cursor.css({ - 'left':(this.audio.currentTime/this.audio.duration * this.timeline_w)+"px" - }); - // update time text display - var rem = parseInt(this.audio.currentTime, 10), - mins = Math.floor(rem/60,10), - secs = rem - mins*60; - this.$currentTime.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); - }, - onEnded(){ - this.emmit('audio-ended'); - this.stop(); - }, - // cartel functions - loadNode(nid){ - this.$cartel.addClass('loading'); - $.getJSON('/edlp/ajax/json/node/'+nid+'/player_cartel', {}) - .done(this.onNodeLoaded.bind(this)) - .fail(this.onNodeLoadFail.bind(this)); - }, - onNodeLoaded(data){ - console.log('AudioPlayer node loaded'); - this.$cartel.html(data.rendered).removeClass('loading'); - _$body.trigger({'type':'new-audio-cartel-loaded'}); - initAjaxLinks(); - }, - onNodeLoadFail(jqxhr, textStatus, error){ - console.warn('AudioPlayer node load failed', jqxhr.responseText); - this.$cartel.removeClass('loading').html(''); - }, - // global - show(){ - this.$container.addClass('visible'); - }, - showHidePreviousBtn(){ - if(this.historic.length > 1 && this.currentHistoricIndex > 0){ - this.$previous.addClass('is-active'); - }else{ - this.$previous.removeClass('is-active'); - } - }, - showHideNextBtn(){ - if(this.currentHistoricIndex < this.historic.length-1 || this.shuffle_is_active){ - this.$next.addClass('is-active'); - }else{ - this.$next.removeClass('is-active'); - } - }, - timeOutToHide(){ - console.log('AudioPlayer timeOutToHide()'); - this.clearTimeOutToHide(); - this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS); - }, - clearTimeOutToHide(){ - console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer); - if(this.hideTimer){ - clearTimeout(this.hideTimer); - this.hideTimer = false; - } - }, - hide(){ - console.log('AudioPlayer hide()'); - this.$container.removeClass('visible'); - // trigger highlighted node remove on corpus map - _$corpus_canvas.trigger('audio-node-closed'); - }, - // object events - on(event_name, handler){ - if(typeof this.event_handlers[event_name] == 'undefined'){ - console.warn('AudioPlayer : event '+event_name+' does not exists'); - } - this.event_handlers[event_name].push(handler); - return this; - }, - emmit(event_name){ - var handler; - for (var i = this.event_handlers[event_name].length; i >= 0 ; i--) { - handler = this.event_handlers[event_name][i]; - setTimeout(handler, 0); - } - return this; - }, + .on('search-results-loaded', initAjaxLinks) + .on('open_entree', closeAllModals) + .on('close_entree', backToFrontPage); } // ___ _ _ ___ @@ -365,7 +114,7 @@ // Audio links if($link.is('.audio-link')){ // TODO: stop randomplayer - _audio_player + _audioPlayer .emmit('stop-shuffle') .openDocument({ nid:$link.attr('nid'), @@ -521,7 +270,7 @@ }) .on('corpus-cliked-on-node', function(e) { console.log('theme : corpus-cliked-on-node', e); - _audio_player + _audioPlayer .emmit('stop-shuffle') .openDocument(e.target_node); }); @@ -531,6 +280,284 @@ _$body.attr('corpus-map', 'ready'); } + + // _ _ _ + // /_\ _ _ __| (_)___ + // / _ \ || / _` | / _ \ + // /_/ \_\_,_\__,_|_\___/ + // + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement + // https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/samples/gg589528%28v%3dvs.85%29 + // https://www.binarytides.com/using-html5-audio-element-javascript/ + function AudioPlayer(){ + var that = this; + this.fid; + this.audio = new Audio(); + // audio events + this.audio_events = ["loadedmetadata","canplay","playing","pause","timeupdate","ended"]; + + // UI dom objects + this.$container = $('
    '); + // btns + this.$btns = $('
    ').addClass('btns').appendTo(this.$container); + this.$previous = $('
    ').addClass('previous').appendTo(this.$btns); + this.$playpause = $('
    ').addClass('play-pause').appendTo(this.$btns); + this.$next = $('
    ').addClass('next').appendTo(this.$btns); + // timeline + this.$timelinecont= $('
    ').addClass('time-line-container').appendTo(this.$container); + this.$timeline = $('
    ').addClass('time-line').appendTo(this.$timelinecont); + this.$loader = $('
    ').addClass('loader').appendTo(this.$timeline); + this.$cursor = $('
    ').addClass('cursor').appendTo(this.$timeline); + // time + this.$time = $('
    ').addClass('time').appendTo(this.$container); + this.$currentTime = $('
    ').addClass('current-time').html('00:00').appendTo(this.$time); + this.$duration = $('
    ').addClass('duration').html('00:00').appendTo(this.$time); + // favoris + this.$fav = $('
    ').addClass('favoris').appendTo(this.$container); + // cartel + this.$cartel = $('
    ').addClass('cartel').appendTo(this.$container); + + // hiding + this.hideTimer = false; + this.hideTimeMS = 10000; + + // history + this.currentHistoricIndex = null; + this.historic = []; + this.shuffle_is_active = false; + + // object events + this.event_handlers = { + 'audio-open-document':[], + 'audio-play':[], + 'audio-pause':[], + 'audio-play-next':[], + 'audio-ended':[], + 'stop-shuffle':[] + }; + + this.init(); + }; + AudioPlayer.prototype = { + init(){ + // append ui to document + this.$container.appendTo('header[role="banner"] .region-header'); + // record timeline width + this.timeline_w = parseInt(this.$timeline.width()); + // init audio events + var fn = ''; + for (var i = 0; i < this.audio_events.length; i++) { + fn = this.audio_events[i]; + // capitalize first letter of event (only cosmetic :p ) + fn = 'on'+fn.charAt(0).toUpperCase()+fn.slice(1); + this.audio.addEventListener( + this.audio_events[i], + this[fn].bind(this), + true); + } + // init btns events + this.$previous.on('click', this.playPrevious.bind(this)); + this.$playpause.on('click', this.togglePlayPause.bind(this)); + this.$next.on('click', this.playNext.bind(this)); + // TODO: previous and next btns + }, + openDocument(node, caller){ + // console.log('AudioPlayer openDocument', node); + if(typeof node == 'undefined' + || typeof node.nid == 'undefined' + || typeof node.audio_url == 'undfined'){ + console.warn('AudioPlayer openDocument() node is malformed', node); + return false; + } + this.historic.push(node); + this.currentHistoricIndex = this.historic.length-1; + // this.shuffle_mode = shuffle_mode || false; + + this.emmit('audio-open-document', {caller:caller}); + + this.launch(); + }, + launch(){ + this.clearTimeOutToHide(); + this.setSRC(this.historic[this.currentHistoricIndex].audio_url); + this.loadNode(this.historic[this.currentHistoricIndex].nid); + // emmit new playing doc (e.g.: corpus map nowing that audio played from RandomPlayer) + try { + _$corpus_canvas.trigger({ + 'type':'audio-node-opened', + 'nid':this.historic[this.currentHistoricIndex].nid + }); + } catch (e) { + console.info('AudioPlayer : _$corpus_canvas does not exists'); + } + + this.showHidePreviousBtn(); + this.showHideNextBtn(); + this.show(); + }, + // audio functions + setSRC(url){ + // console.log('AudioPlayer setSRC : url', url); + this.audio.src = url; + }, + onLoadedmetadata(){ + var rem = parseInt(this.audio.duration, 10), + mins = Math.floor(rem/60,10), + secs = rem - mins*60; + this.$duration.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); + this.updateLoadingBar(); + }, + updateLoadingBar(){ + this.$loader.css({ + 'width':parseInt((100 * this.audio.buffered.end(0) / this.audio.duration), 10)+'%' + }); + if( this.audio.buffered.end(0) < this.audio.duration ){ + // loop through this function until file is fully loaded + var that = this; + window.requestAnimationFrame(that.updateLoadingBar.bind(that)); + }else{ + console.log('Audio fully loaded'); + } + }, + onCanplay(){ + this.play(); + }, + play(){ + this.clearTimeOutToHide(); + this.audio.play(); + }, + playPrevious(){ + if(this.currentHistoricIndex > 0){ + this.currentHistoricIndex -= 1; + this.launch(); + } + }, + playNext(){ + if(this.currentHistoricIndex < this.historic.length-1){ + this.currentHistoricIndex += 1; + this.launch(); + }else{ + this.emmit('audio-play-next'); + } + }, + togglePlayPause(e){ + if(this.audio.paused){ + this.audio.play(); + }else{ + this.audio.pause(); + } + }, + stop(){ + // console.log('AudioPlayer stop()'); + this.audio.pause(); + this.timeOutToHide(); + }, + // audio events + onPlaying(){ + this.$btns.addClass('is-playing'); + this.emmit('audio-play'); + }, + onPause(){ + this.$btns.removeClass('is-playing'); + this.emmit('audio-pause'); + }, + onTimeupdate(){ + // move cursor + this.$cursor.css({ + 'left':(this.audio.currentTime/this.audio.duration * this.timeline_w)+"px" + }); + // update time text display + var rem = parseInt(this.audio.currentTime, 10), + mins = Math.floor(rem/60,10), + secs = rem - mins*60; + this.$currentTime.html(''+(mins<10 ? '0':'')+mins+':'+(secs<10 ? '0':'')+secs+''); + }, + onEnded(){ + this.emmit('audio-ended'); + this.stop(); + }, + // cartel functions + loadNode(nid){ + this.$cartel.addClass('loading'); + $.getJSON('/edlp/ajax/json/node/'+nid+'/player_cartel', {}) + .done(this.onNodeLoaded.bind(this)) + .fail(this.onNodeLoadFail.bind(this)); + }, + onNodeLoaded(data){ + // console.log('AudioPlayer node loaded'); + this.$cartel.html(data.rendered).removeClass('loading'); + _$body.trigger({'type':'new-audio-cartel-loaded'}); + initAjaxLinks(); + }, + onNodeLoadFail(jqxhr, textStatus, error){ + console.warn('AudioPlayer node load failed', jqxhr.responseText); + this.$cartel.removeClass('loading').html(''); + }, + // global + show(){ + this.$container.addClass('visible'); + }, + showHidePreviousBtn(){ + if(this.historic.length > 1 && this.currentHistoricIndex > 0){ + this.$previous.addClass('is-active'); + }else{ + this.$previous.removeClass('is-active'); + } + }, + showHideNextBtn(){ + if(this.currentHistoricIndex < this.historic.length-1 || this.shuffle_is_active){ + this.$next.addClass('is-active'); + }else{ + this.$next.removeClass('is-active'); + } + }, + timeOutToHide(){ + // console.log('AudioPlayer timeOutToHide()'); + this.clearTimeOutToHide(); + this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS); + }, + clearTimeOutToHide(){ + // console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer); + if(this.hideTimer){ + clearTimeout(this.hideTimer); + this.hideTimer = false; + } + }, + hide(){ + // console.log('AudioPlayer hide()'); + this.$container.removeClass('visible'); + // trigger highlighted node remove on corpus map + try { + _$corpus_canvas.trigger('audio-node-closed'); + } catch (e) { + console.info('AudioPlayer hide() : _$corpus_canvas does not exists'); + } + }, + // object events + on(event_name, handler){ + if(typeof this.event_handlers[event_name] == 'undefined'){ + console.warn('AudioPlayer : event '+event_name+' does not exists'); + } + this.event_handlers[event_name].push(handler); + return this; + }, + emmit(event_name, args){ + // console.log('AudioPlayer emmit() event_name', event_name); + // console.log('AudioPlayer emmit() handlers', this.event_handlers[event_name]); + var handler; + var args = args || {}; + for (var i = this.event_handlers[event_name].length-1; i >= 0 ; i--) { + handler = this.event_handlers[event_name][i]; + // console.log('AudioPlayer emmit() loop handler', handler); + setTimeout(function(){ + // console.log('AudioPlayer emmit() timeout handler', handler); + handler(args); + }, 0); + } + return this; + }, + } + // ___ _ ___ _ // | _ \__ _ _ _ __| |___ _ __ | _ \ |__ _ _ _ ___ _ _ // | / _` | ' \/ _` / _ \ ' \| _/ / _` | || / -_) '_| @@ -540,7 +567,7 @@ this.active = false; this.playlist = playlist; this.$btn = $('').html('Shuffle').addClass('random-player-btn'); - this.init() + this.init(); }; RandomPlayer.prototype = { init(){ @@ -554,7 +581,7 @@ this.$btn.on('click', this.toggleActive.bind(this)); // attach an event on AudioPlayer - _audio_player + _audioPlayer .on('audio-ended', this.onAudioPlayerEnded.bind(this)) .on('audio-play-next', this.onAudioPlayNext.bind(this)) .on('stop-shuffle', this.stop.bind(this)); @@ -579,20 +606,20 @@ } }, start(){ - this.active = _audio_player.shuffle_is_active = true; + this.active = _audioPlayer.shuffle_is_active = true; this.$btn.addClass('is-active'); this.shuffle(); this.next(); }, stop(){ - this.active = _audio_player.shuffle_is_active = false; + this.active = _audioPlayer.shuffle_is_active = false; this.$btn.removeClass('is-active'); // stop audio player - // _audio_player.stop(); + // _audioPlayer.stop(); }, next(){ if(this.active && this.shuffledPlaylist.length > 0) - _audio_player.openDocument(this.shuffledPlaylist.splice(0,1)[0]); + _audioPlayer.openDocument(this.shuffledPlaylist.splice(0,1)[0]); }, onAudioPlayNext(){ console.log('RandomPlayer : onAudioPlayNext()'); @@ -604,6 +631,215 @@ } }; + // ___ ___ _ + // / __|___ _ __ _ __ ___| _ \ |__ _ _ _ ___ _ _ + // | (__/ _ \ ' \| '_ \/ _ \ _/ / _` | || / -_) '_| + // \___\___/_|_|_| .__/\___/_| |_\__,_|\_, \___|_| + // |_| |__/ + function CompoPlayer(){ + this.playing = false; + this.paused = false; + this.playlist = []; + this.current_index = 0; + this.$compo = null; + this.init(); + }; + CompoPlayer.prototype = { + init(){ + console.log('CompoPlayer init()'); + // attach an event on AudioPlayer + _audioPlayer + .on('audio-open-document', this.onAudioOpenDocument.bind(this)) + .on('audio-play', this.onAudioPlayerPlay.bind(this)) + .on('audio-pause', this.onAudioPlayerPause.bind(this)) + .on('audio-ended', this.onAudioPlayerEnded.bind(this)); + // .on('audio-play-next', this.onAudioPlayNext.bind(this)); + + this.newCompo(); + }, + newCompo(){ + console.log('CompoPlayer newCompo()'); + // $('.composition_ui .composer .composition .field--name-documents') + this.$composer = $('.composition_ui .composer'); + this.$compo = $('.composition_ui .composer .composition'); + this.$controls = $('.composition_ui .composer .compo-player-controls'); + this.$previous = $('
    ').addClass('previous') + .on('click', this.prev.bind(this)) + .appendTo(this.$controls); + this.$playpause = $('
    ').addClass('play-pause') + .on('click', this.togglePlayPause.bind(this)) + .appendTo(this.$controls); + this.$next = $('
    ').addClass('next') + .on('click', this.next.bind(this)) + .appendTo(this.$controls); + // this.$playpause = $('.compo-player-controls', this.$composer) + // .on('click', this.togglePlayPause.bind(this)); + this.refresh(); + }, + refresh(){ + // console.log('CompoPlayer refresh(), this', this); + // this.playing = false; + // this.paused = false; + // this.resetIndex(); + this.stop(); + + // load new playlist + this.playlist = []; + var that = this; + $('.field--name-documents .field__item',this.$compo).each(function(i,el){ + var $link = $('a.audio-link',this); + that.playlist.push({ + item:$(this), + audio_url:$link.attr("audio_url"), + nid:$link.attr("nid"), + }); + }); + this.showHideControls(); + }, + togglePlayPause(){ + // console.log('CompoPlayer togglePlayPause'); + if (this.playing && !this.paused) { + this.pause(); + }else{ + if(this.playing && this.paused){ + this.play(); + }else{ + this.start(); + } + } + }, + start(){ + console.log('start'); + // console.log('CompoPlayer start()'); + this.playing = true; + this.play(); + }, + play(){ + // console.log('play'); + if(this.paused){ + this.paused = false; + _audioPlayer.play(); + }else{ + _audioPlayer.openDocument(this.playlist[this.current_index], this); + } + this.setActiveItem().showHideControls(); + }, + pause(){ + console.log('pause'); + this.paused = true; + this.showHideControls(); + _audioPlayer.stop(); + }, + next(){ + // console.log('CompoPlayer next()'); + if(this.playing){ + this.current_index += 1; + if(this.current_index < this.playlist.length){ + this.play(); + }else{ + this.stop(); + } + } + }, + prev(){ + // console.log('CompoPlayer prev()'); + if(this.playing){ + this.current_index -= 1; + if(this.current_index >= 0){ + this.play(); + }else{ + this.stop(); + } + } + }, + stop(){ + _audioPlayer.stop(); + this.reset(); + }, + reset(){ + this.playing = false; + this.paused = false; + this.resetIndex(); + }, + resetIndex(){ + this.current_index = 0; + this.showHideControls().resetActiveItems(); + }, + setActiveItem(){ + this.resetActiveItems(); + if(this.playing && this.current_index >= 0){ + this.playlist[this.current_index].item.addClass('is-active'); + } + // this call shoud not be here + this.showHideControls(); + return this; + }, + resetActiveItems(){ + for (var n = 0; n < this.playlist.length; n++) { + // console.log('node',node); + this.playlist[n].item.removeClass('is-active'); + } + return this; + }, + showHideControls(){ + // console.log('CompoPlayer showHideNextBtn(), playing:'+this.playing+', paused:'+this.paused); + // global playing + if(this.playing && !this.paused){ + this.$controls.addClass('is-playing'); + }else{ + this.$controls.removeClass('is-playing'); + } + // playpause + if(this.playlist.length > 0){ + this.$playpause.addClass('is-active'); + }else{ + this.$playpause.removeClass('is-active'); + } + // next + if(this.playing && this.playlist.length > 1 && this.current_index < this.playlist.length -1){ + this.$next.addClass('is-active'); + }else{ + this.$next.removeClass('is-active'); + } + // previous + if(this.playing && this.playlist.length > 1 && this.current_index > 0){ + this.$previous.addClass('is-active'); + }else{ + this.$previous.removeClass('is-active'); + } + return this; + }, + // _audioPlayer events + onAudioOpenDocument(args){ + if(args.caller !== this){ + // console.log('CompoPlayer onAudioOpenDocument() called by other'); + this.reset(); + } + // else{ + // // console.log('CompoPlayer onAudioOpenDocument() self calling'); + // } + }, + onAudioPlayerPlay(){ + if(this.playing && this.paused){ + this.paused = false; + this.showHideControls(); + } + }, + onAudioPlayerPause(){ + if(this.playing && !this.paused){ + this.paused = true; + this.showHideControls(); + } + }, + onAudioPlayerEnded(){ + this.next(); + }, + // onAudioPlayNext(){ + // this.next(); + // } + }; + + // ___ _ _ _ // | _ \_ _ ___ __| |_ _ __| |_(_)___ _ _ ___ // | _/ '_/ _ \/ _` | || / _| _| / _ \ ' \(_-< diff --git a/sites/all/themes/custom/edlptheme/assets/styles/app.scss b/sites/all/themes/custom/edlptheme/assets/styles/app.scss index 7fa6f101b..3df77103d 100644 --- a/sites/all/themes/custom/edlptheme/assets/styles/app.scss +++ b/sites/all/themes/custom/edlptheme/assets/styles/app.scss @@ -258,6 +258,39 @@ main[role="main"]{ // / _ \ || / _` | / _ \ _/ / _` | || / -_) '_| // /_/ \_\_,_\__,_|_\___/_| |_\__,_|\_, \___|_| // |__/ +@mixin audio_controls { + // outline: 1px dotted orange; + &>*{ + display: inline-block; + vertical-align: middle; + width:20px;height:30px; + background-position: center; + background-size: contain; + } + .play-pause{ + background-image: url(../img/audio-player-play.svg); + padding:0 0.3em; + cursor: pointer; + } + &.is-playing{ + .play-pause{background-image: url(../img/audio-player-pause.svg);} + } + .previous, .next{ + opacity: 0.3; + transition: opacity 0.3s ease-in-out; + cursor: auto; + &.is-active{ + opacity: 1; + cursor: pointer; + } + } + .previous{ + background-image: url(../img/audio-player-previous.svg); + } + .next{ + background-image: url(../img/audio-player-next.svg); + } +} #audio-player{ position: absolute; top:0; left:0; @@ -281,34 +314,7 @@ main[role="main"]{ // outline: 1px solid green; } .btns{ - // outline: 1px dotted orange; - &>*{ - display: inline-block; - vertical-align: middle; - width:20px;height:30px; - background-position: center; - background-size: contain; - } - .play-pause{ - background-image: url(../img/audio-player-play.svg); - padding:0 0.3em; - cursor: pointer; - } - .previous, .next{ - opacity: 0.3; - transition: opacity 0.3s ease-in-out; - cursor: auto; - &.is-active{ - opacity: 1; - cursor: pointer; - } - } - .previous{ - background-image: url(../img/audio-player-previous.svg); - } - .next{ - background-image: url(../img/audio-player-next.svg); - } + @include audio_controls; } .time-line-container{ .time-line{ @@ -413,9 +419,7 @@ main[role="main"]{ } } } - &.is-playing{ - .btns .play-pause{background-image: url(../img/audio-player-pause.svg);} - } + } @@ -473,18 +477,16 @@ main[role="main"]{ #studio-ui{ height:100%; @mixin draggable_sortable_field__item { - // outline: 1px dotted green; display: inline-block; position: relative; + white-space: nowrap; + pointer-events: none; // margin-right: 25px; width:25px; height: 100%; &.ui-draggable-dragging{ height:100px; - // cursor: grabbing; } - white-space: nowrap; - pointer-events: none; opacity: 1; transition: opacity 0.1s ease-in-out; &.ready-to-remove{ @@ -504,6 +506,7 @@ main[role="main"]{ margin:0; text-transform: none; a{ + white-space: nowrap!important; &:before{ content:""; display: inline-block; @@ -530,6 +533,9 @@ main[role="main"]{ background-color: white; // TODO: entries color; } + &.is-active .handler{ + background-color: red; + } } // end mixin .chutier_ui{ @@ -652,6 +658,9 @@ main[role="main"]{ vertical-align: middle; margin-right: $m; } + &.ajax-loading:before{ + @include spining-loader-square; + } &.is-active:before{ background-color: black; } @@ -718,12 +727,18 @@ main[role="main"]{ $header_height:25px; $actions_height:25px; $padding:5px; + opacity:1; + transition: opacity 0.3s ease-in-out; + &.ajax-loading{ + opacity:0.3; + } >*{ box-sizing: border-box; } >header{ height:$header_height; padding-bottom:$padding; + font-size: 0.756em; } >.composition{ position: relative; @@ -772,10 +787,14 @@ main[role="main"]{ padding-top:$padding; height:$actions_height; // outline: 1px solid blue; + .compo-player-controls{ + @include audio_controls; + } } } } } + // drag and drop specifics // created by js .field__item.ui-draggable-dragging{ diff --git a/sites/all/themes/custom/edlptheme/edlptheme.theme b/sites/all/themes/custom/edlptheme/edlptheme.theme index c9de6dde6..2354111df 100644 --- a/sites/all/themes/custom/edlptheme/edlptheme.theme +++ b/sites/all/themes/custom/edlptheme/edlptheme.theme @@ -103,7 +103,6 @@ function edlptheme_preprocess_node__enregistrement__search_index(&$vars){ // dpm($vars['link_attributes']); } - function edlptheme_preprocess_node__enregistrement__compo(&$vars){ $node = $vars['elements']['#node']; $options = ['absolute' => TRUE]; @@ -125,6 +124,10 @@ function edlptheme_preprocess_node__enregistrement__compo(&$vars){ 'nid' => $node->id(), 'class' => array('audio-link', 'ajax-link') )); + + // dpm($vars); + $title = $vars['label'][0]['#context']['value']; + $vars['label'][0]['#context']['value'] = Unicode::truncate($title, 25, true, true); // dpm($vars['link_attributes']); } diff --git a/sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig b/sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig index 9c5ca841f..b2fe7cb9e 100644 --- a/sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig +++ b/sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig @@ -84,8 +84,9 @@ - {% set text = label|render|striptags %} - {{ text|length > 25 ? text|slice(0, 25)|split(' ')|slice(0, -1)|join(' ') ~ '…' : text }} + {{ label }} + {# set text = label|render|striptags #} + {# text|length > 25 ? text|slice(0, 25)|split(' ')|slice(0, -1)|join(' ') ~ '…' : text #} diff --git a/sites/default/config/sync/core.entity_view_display.composition.composition.default.yml b/sites/default/config/sync/core.entity_view_display.composition.composition.default.yml index 2a4ab6dec..7937bde48 100644 --- a/sites/default/config/sync/core.entity_view_display.composition.composition.default.yml +++ b/sites/default/config/sync/core.entity_view_display.composition.composition.default.yml @@ -12,13 +12,20 @@ content: documents: label: hidden type: entity_reference_entity_view - weight: 0 + weight: 1 region: content settings: view_mode: compo link: false third_party_settings: { } + name: + type: string + weight: 0 + region: content + label: hidden + settings: + link_to_entity: false + third_party_settings: { } hidden: langcode: true - name: true user_id: true diff --git a/sites/default/config/sync/core.entity_view_display.composition.composition.studio_ui.yml b/sites/default/config/sync/core.entity_view_display.composition.composition.studio_ui.yml new file mode 100644 index 000000000..815124992 --- /dev/null +++ b/sites/default/config/sync/core.entity_view_display.composition.composition.studio_ui.yml @@ -0,0 +1,26 @@ +uuid: d063513e-e6a2-471a-93e0-9b02fa1117d9 +langcode: fr +status: true +dependencies: + config: + - core.entity_view_mode.composition.studio_ui + module: + - edlp_studio +id: composition.composition.studio_ui +targetEntityType: composition +bundle: composition +mode: studio_ui +content: + documents: + label: hidden + type: entity_reference_entity_view + weight: 0 + region: content + settings: + view_mode: compo + link: false + third_party_settings: { } +hidden: + langcode: true + name: true + user_id: true diff --git a/sites/default/config/sync/core.entity_view_mode.composition.studio_ui.yml b/sites/default/config/sync/core.entity_view_mode.composition.studio_ui.yml new file mode 100644 index 000000000..39c98342a --- /dev/null +++ b/sites/default/config/sync/core.entity_view_mode.composition.studio_ui.yml @@ -0,0 +1,10 @@ +uuid: eff6d988-0963-4718-ad52-bf10a8e1463d +langcode: fr +status: true +dependencies: + module: + - edlp_studio +id: composition.studio_ui +label: studio-ui +targetEntityType: composition +cache: true