Browse Source

studio-ui composer is ready to test actively

Bachir Soussi Chiadmi 6 years ago
parent
commit
58ffc1ff88

+ 14 - 12
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($('<li>').append(data.new_link));
+        .before($('<li>').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);

+ 5 - 3
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();

+ 3 - 1
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 ?)
       ),
     );

+ 487 - 251
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,22 +44,243 @@
     // |___|\_/\___|_||_\__/__/
     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('search-results-loaded', initAjaxLinks)
+        .on('open_entree', closeAllModals)
+        .on('close_entree', backToFrontPage);
+    }
+
+    //  ___             _ _ ___
+    // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
+    // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
+    // |___/\__|_| \___/_|_|___/\__,_|_| /__/
+    function initScrollbars(){
+      // console.log("initScrollbars");
+      // TODO: find a better js scroll than overlayScrollbars which does not handle well max-height + overflow-y:auto;
+      // $('.os-scroll').overlayScrollbars({
+      //   overflowBehavior:{
+      //     x:'h',
+      //     y:'scroll',
+      //     clipAlways:false
+      //   }
+      // });
+    };
+
+    //    _    _
+    //   /_\  (_)__ ___ __
+    //  / _ \ | / _` \ \ /
+    // /_/ \_\/ \__,_/_\_\
+    //      |__/
+    // TODO: add url hash nav
+    // TODO: implement history.js
+    function initAjaxLinks(){
+      console.log('initAjaxLinks');
+
+      $('a', '#block-mainnavigation')
+        .add('a', '#block-footer.menu--footer')
+        .add('a', '#block-productions')
+        .add('a', 'article.node:not(.node--type-enregistrement) h2.node-title')
+        .add('a', '.productions-subtree')
+        .add('a', '.productions-parent')
+        // .add('a.index-link, a.notice-link', '#block-edlpentreesblock')
+        .addClass('ajax-link');
+
+
+      _$ajaxLinks = $('.ajax-link:not(.ajax-enabled)')
+        .each(function(i,e){
+          var $this = $(this);
+          // avoid already ajaxified links
+          if($this.is('.ajax-enable')) return;
+          if($this.attr('data-drupal-link-system-path')){
+            $this.on('click', onClickAjaxLink).addClass('ajax-enable');
+          }
+        });
+    };
+    function onClickAjaxLink(e){
+      e.preventDefault();
+      var $link = $(this);
+
+      if($link.is('.is-active'))
+        return false;
+
+      // Audio links
+      if($link.is('.audio-link')){
+        // TODO: stop randomplayer
+        _audioPlayer
+          .emmit('stop-shuffle')
+          .openDocument({
+            nid:$link.attr('nid'),
+            audio_url:$link.attr('audio_url')
+          });
+        return false;
+      }
+
+      // other links
+      var sys_path = $(this).attr('data-drupal-link-system-path');
+      var ajax_path = sys_path;
+      if(sys_path == '<front>'){
+        backToFrontPage();
+        return false;
+      }
+
+      // convert node link to edlp_ajax_node module links
+      var node_match = ajax_path.match(/^\/?(node\/\d+)$/g);
+      var term_match = ajax_path.match(/^\/?(taxonomy\/term\/\d+)$/g);
+      if(node_match){
+        ajax_path = 'edlp/ajax/json/'+node_match[0];
+        // check for viewmode attribute
+        if($link.attr('viewmode')){
+          ajax_path += '/'+$link.attr('viewmode');
+        }
+      }else if(term_match){
+        ajax_path = 'edlp/ajax/json/'+term_match[0];
+        ajax_path = ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
+        // check for viewmode attribute
+        if($link.attr('viewmode')){
+          ajax_path += '/'+$link.attr('viewmode');
+        }
+      }else{
+        // convert other link to ajax
+        ajax_path += '/ajax'
+      }
+
+      _$body.addClass('ajax-loading');
+      $link.addClass('ajax-loading');
+
+      // TODO: use Drupal.url()
+      // Drupal.url = function (path) {
+      //   return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
+      // };
+
+      var path = window.location.origin + Drupal.url(ajax_path);
+      $.getJSON(path, {})
+        .done(function(data){
+          onAjaxLinkLoaded(data, $link, sys_path);
         })
-        .on('close_entree', function(e){
-          // e.tid available
+        .fail(function(jqxhr, textStatus, error){
+          onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path);
+        });
+
+      return false;
+    };
+    function onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path){
+      console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
+      $link.removeClass('ajax-loading');
+      _$body.removeClass('ajax-loading');
+    };
+    function onAjaxLinkLoaded(data, $link, sys_path){
+      console.log('ajax link loaded : data', data);
+      _$body.removeClass('ajax-loading');
+
+      // replace all content with newly loaded
+      // TODO: build a system to replace or append contents (like studio + search)
+      _$row.html(data.rendered);
+
+      // add close btn
+      if(sys_path != 'procuction'){
+        addCloseBtnToCols();
+      }
+
+      // add body class for currently loaded content
+      var body_classes = [
+        'path-'+sys_path.replace(/\//g, '-'),
+        'entity-type-'+data.entity_type,
+        'bundle-'+data.bundle,
+        'view-mode-'+data.view_mode
+      ];
+      _$body.removeClass().addClass(body_classes.join(' '));
+
+      // id node add a generic path-node class to body
+      m = sys_path.match(/^\/?(node\/\d+)$/g);
+      if(m)
+        _$body.addClass('path-edlp-node');
+
+      // handle clicked link classes
+      _$ajaxLinks.removeClass('is-active');
+      $link.removeClass('ajax-loading').addClass('is-active');
+
+      // if block attached (eg : from edlp_productions module)
+      if(typeof data.block != 'undefined'){
+        // if block not already added
+        if(!$('#'+data.block.id, '.region-'+data.block.region).length){
+          $('.region-'+data.block.region).append(data.block.rendered);
+        }
+      }
+
+      initScrollbars();
+
+      if(sys_path == "productions")
+        initProductions();
+
+      initAjaxLinks();
+
+      _$body.trigger({'type':'new-content-ajax-loaded'});
+
+      // call behaviours
+      Drupal.attachBehaviors(_$row[0]);
+
+    };
+
+    function addCloseBtnToCols(){
+      $('.col', _$row).each(function(index, el) {
+
+        if($('span.close-col-btn', this).length)
+          return true;
+
+        $(this).children('.wrapper').append($('<span>')
+          .addClass('close-col-btn')
+          .on('click', function(e){
+            // check for theme attribute and emmit event
+            var $col = $(this).parents('.col');
+            var theme = $col.attr('theme');
+            if(theme != ''){
+              _$body.trigger({'type':theme+'-col-closed'});
+            }
+            // remove the col
+            $col.remove();
+            // if row is empty call closeAllModals()
+            if(!$('.col', _$row).length){
+              backToFrontPage();
+            }
+          })
+        );
+      });
+    };
+
+    //   ___
+    //  / __|___ _ _ _ __ _  _ ___
+    // | (__/ _ \ '_| '_ \ || (_-<
+    //  \___\___/_| | .__/\_,_/__/
+    //              |_|
+    function onCorpusMapReady(e){
+      console.log('theme : onCorpusReady', e);
+      _$corpus_canvas = $('canvas#corpus-map');
+      _$corpus_canvas
+        .on('corpus-cliked-on-map', function(e) {
+          console.log('theme : corpus-cliked-on-map');
           backToFrontPage();
+        })
+        .on('corpus-cliked-on-node', function(e) {
+          console.log('theme : corpus-cliked-on-node', e);
+          _audioPlayer
+            .emmit('stop-shuffle')
+            .openDocument(e.target_node);
         });
+
+      _randomPlayer = new RandomPlayer(e.playlist);
+
+      _$body.attr('corpus-map', 'ready');
     }
 
+
     //    _          _ _
     //   /_\ _  _ __| (_)___
     //  / _ \ || / _` | / _ \
@@ -105,6 +328,9 @@
 
       // object events
       this.event_handlers = {
+        'audio-open-document':[],
+        'audio-play':[],
+        'audio-pause':[],
         'audio-play-next':[],
         'audio-ended':[],
         'stop-shuffle':[]
@@ -135,11 +361,20 @@
         this.$next.on('click', this.playNext.bind(this));
         // TODO: previous and next btns
       },
-      openDocument(node){
-        console.log('AudioPlayer openDocument', node);
+      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(){
@@ -153,7 +388,7 @@
             'nid':this.historic[this.currentHistoricIndex].nid
           });
         } catch (e) {
-          console.warn('AudioPlayer : _$corpus_canvas does not exists');
+          console.info('AudioPlayer : _$corpus_canvas does not exists');
         }
 
         this.showHidePreviousBtn();
@@ -188,6 +423,7 @@
         this.play();
       },
       play(){
+        this.clearTimeOutToHide();
         this.audio.play();
       },
       playPrevious(){
@@ -212,16 +448,18 @@
         }
       },
       stop(){
-        console.log('AudioPlayer stop()');
+        // console.log('AudioPlayer stop()');
         this.audio.pause();
         this.timeOutToHide();
       },
       // audio events
       onPlaying(){
-        this.$container.addClass('is-playing');
+        this.$btns.addClass('is-playing');
+        this.emmit('audio-play');
       },
       onPause(){
-        this.$container.removeClass('is-playing');
+        this.$btns.removeClass('is-playing');
+        this.emmit('audio-pause');
       },
       onTimeupdate(){
         // move cursor
@@ -246,7 +484,7 @@
           .fail(this.onNodeLoadFail.bind(this));
       },
       onNodeLoaded(data){
-        console.log('AudioPlayer node loaded');
+        // console.log('AudioPlayer node loaded');
         this.$cartel.html(data.rendered).removeClass('loading');
         _$body.trigger({'type':'new-audio-cartel-loaded'});
         initAjaxLinks();
@@ -274,22 +512,26 @@
         }
       },
       timeOutToHide(){
-        console.log('AudioPlayer timeOutToHide()');
+        // console.log('AudioPlayer timeOutToHide()');
         this.clearTimeOutToHide();
         this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS);
       },
       clearTimeOutToHide(){
-        console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer);
+        // console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer);
         if(this.hideTimer){
           clearTimeout(this.hideTimer);
           this.hideTimer = false;
         }
       },
       hide(){
-        console.log('AudioPlayer hide()');
+        // console.log('AudioPlayer hide()');
         this.$container.removeClass('visible');
         // trigger highlighted node remove on corpus map
-        _$corpus_canvas.trigger('audio-node-closed');
+        try {
+          _$corpus_canvas.trigger('audio-node-closed');
+        } catch (e) {
+          console.info('AudioPlayer hide() : _$corpus_canvas does not exists');
+        }
       },
       // object events
       on(event_name, handler){
@@ -299,238 +541,23 @@
         this.event_handlers[event_name].push(handler);
         return this;
       },
-      emmit(event_name){
+      emmit(event_name, args){
+        // console.log('AudioPlayer emmit() event_name', event_name);
+        // console.log('AudioPlayer emmit() handlers', this.event_handlers[event_name]);
         var handler;
-        for (var i = this.event_handlers[event_name].length; i >= 0 ; i--) {
+        var args  = args || {};
+        for (var i = this.event_handlers[event_name].length-1; i >= 0 ; i--) {
           handler = this.event_handlers[event_name][i];
-          setTimeout(handler, 0);
+          // console.log('AudioPlayer emmit() loop handler', handler);
+          setTimeout(function(){
+            // console.log('AudioPlayer emmit() timeout handler', handler);
+            handler(args);
+          }, 0);
         }
         return this;
       },
     }
 
-    //  ___             _ _ ___
-    // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
-    // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
-    // |___/\__|_| \___/_|_|___/\__,_|_| /__/
-    function initScrollbars(){
-      // console.log("initScrollbars");
-      // TODO: find a better js scroll than overlayScrollbars which does not handle well max-height + overflow-y:auto;
-      // $('.os-scroll').overlayScrollbars({
-      //   overflowBehavior:{
-      //     x:'h',
-      //     y:'scroll',
-      //     clipAlways:false
-      //   }
-      // });
-    };
-
-    //    _    _
-    //   /_\  (_)__ ___ __
-    //  / _ \ | / _` \ \ /
-    // /_/ \_\/ \__,_/_\_\
-    //      |__/
-    // TODO: add url hash nav
-    // TODO: implement history.js
-    function initAjaxLinks(){
-      console.log('initAjaxLinks');
-
-      $('a', '#block-mainnavigation')
-        .add('a', '#block-footer.menu--footer')
-        .add('a', '#block-productions')
-        .add('a', 'article.node:not(.node--type-enregistrement) h2.node-title')
-        .add('a', '.productions-subtree')
-        .add('a', '.productions-parent')
-        // .add('a.index-link, a.notice-link', '#block-edlpentreesblock')
-        .addClass('ajax-link');
-
-
-      _$ajaxLinks = $('.ajax-link:not(.ajax-enabled)')
-        .each(function(i,e){
-          var $this = $(this);
-          // avoid already ajaxified links
-          if($this.is('.ajax-enable')) return;
-          if($this.attr('data-drupal-link-system-path')){
-            $this.on('click', onClickAjaxLink).addClass('ajax-enable');
-          }
-        });
-    };
-    function onClickAjaxLink(e){
-      e.preventDefault();
-      var $link = $(this);
-
-      if($link.is('.is-active'))
-        return false;
-
-      // Audio links
-      if($link.is('.audio-link')){
-        // TODO: stop randomplayer
-        _audio_player
-          .emmit('stop-shuffle')
-          .openDocument({
-            nid:$link.attr('nid'),
-            audio_url:$link.attr('audio_url')
-          });
-        return false;
-      }
-
-      // other links
-      var sys_path = $(this).attr('data-drupal-link-system-path');
-      var ajax_path = sys_path;
-      if(sys_path == '<front>'){
-        backToFrontPage();
-        return false;
-      }
-
-      // convert node link to edlp_ajax_node module links
-      var node_match = ajax_path.match(/^\/?(node\/\d+)$/g);
-      var term_match = ajax_path.match(/^\/?(taxonomy\/term\/\d+)$/g);
-      if(node_match){
-        ajax_path = 'edlp/ajax/json/'+node_match[0];
-        // check for viewmode attribute
-        if($link.attr('viewmode')){
-          ajax_path += '/'+$link.attr('viewmode');
-        }
-      }else if(term_match){
-        ajax_path = 'edlp/ajax/json/'+term_match[0];
-        ajax_path = ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
-        // check for viewmode attribute
-        if($link.attr('viewmode')){
-          ajax_path += '/'+$link.attr('viewmode');
-        }
-      }else{
-        // convert other link to ajax
-        ajax_path += '/ajax'
-      }
-
-      _$body.addClass('ajax-loading');
-      $link.addClass('ajax-loading');
-
-      // TODO: use Drupal.url()
-      // Drupal.url = function (path) {
-      //   return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
-      // };
-
-      var path = window.location.origin + Drupal.url(ajax_path);
-      $.getJSON(path, {})
-        .done(function(data){
-          onAjaxLinkLoaded(data, $link, sys_path);
-        })
-        .fail(function(jqxhr, textStatus, error){
-          onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path);
-        });
-
-      return false;
-    };
-    function onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path){
-      console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
-      $link.removeClass('ajax-loading');
-      _$body.removeClass('ajax-loading');
-    };
-    function onAjaxLinkLoaded(data, $link, sys_path){
-      console.log('ajax link loaded : data', data);
-      _$body.removeClass('ajax-loading');
-
-      // replace all content with newly loaded
-      // TODO: build a system to replace or append contents (like studio + search)
-      _$row.html(data.rendered);
-
-      // add close btn
-      if(sys_path != 'procuction'){
-        addCloseBtnToCols();
-      }
-
-      // add body class for currently loaded content
-      var body_classes = [
-        'path-'+sys_path.replace(/\//g, '-'),
-        'entity-type-'+data.entity_type,
-        'bundle-'+data.bundle,
-        'view-mode-'+data.view_mode
-      ];
-      _$body.removeClass().addClass(body_classes.join(' '));
-
-      // id node add a generic path-node class to body
-      m = sys_path.match(/^\/?(node\/\d+)$/g);
-      if(m)
-        _$body.addClass('path-edlp-node');
-
-      // handle clicked link classes
-      _$ajaxLinks.removeClass('is-active');
-      $link.removeClass('ajax-loading').addClass('is-active');
-
-      // if block attached (eg : from edlp_productions module)
-      if(typeof data.block != 'undefined'){
-        // if block not already added
-        if(!$('#'+data.block.id, '.region-'+data.block.region).length){
-          $('.region-'+data.block.region).append(data.block.rendered);
-        }
-      }
-
-      initScrollbars();
-
-      if(sys_path == "productions")
-        initProductions();
-
-      initAjaxLinks();
-
-      _$body.trigger({'type':'new-content-ajax-loaded'});
-
-      // call behaviours
-      Drupal.attachBehaviors(_$row[0]);
-
-    };
-
-    function addCloseBtnToCols(){
-      $('.col', _$row).each(function(index, el) {
-
-        if($('span.close-col-btn', this).length)
-          return true;
-
-        $(this).children('.wrapper').append($('<span>')
-          .addClass('close-col-btn')
-          .on('click', function(e){
-            // check for theme attribute and emmit event
-            var $col = $(this).parents('.col');
-            var theme = $col.attr('theme');
-            if(theme != ''){
-              _$body.trigger({'type':theme+'-col-closed'});
-            }
-            // remove the col
-            $col.remove();
-            // if row is empty call closeAllModals()
-            if(!$('.col', _$row).length){
-              backToFrontPage();
-            }
-          })
-        );
-      });
-    };
-
-    //   ___
-    //  / __|___ _ _ _ __ _  _ ___
-    // | (__/ _ \ '_| '_ \ || (_-<
-    //  \___\___/_| | .__/\_,_/__/
-    //              |_|
-    function onCorpusMapReady(e){
-      console.log('theme : onCorpusReady', e);
-      _$corpus_canvas = $('canvas#corpus-map');
-      _$corpus_canvas
-        .on('corpus-cliked-on-map', function(e) {
-          console.log('theme : corpus-cliked-on-map');
-          backToFrontPage();
-        })
-        .on('corpus-cliked-on-node', function(e) {
-          console.log('theme : corpus-cliked-on-node', e);
-          _audio_player
-            .emmit('stop-shuffle')
-            .openDocument(e.target_node);
-        });
-
-      _randomPlayer = new RandomPlayer(e.playlist);
-
-      _$body.attr('corpus-map', 'ready');
-    }
-
     //  ___              _           ___ _
     // | _ \__ _ _ _  __| |___ _ __ | _ \ |__ _ _  _ ___ _ _
     // |   / _` | ' \/ _` / _ \ '  \|  _/ / _` | || / -_) '_|
@@ -540,7 +567,7 @@
       this.active = false;
       this.playlist = playlist;
       this.$btn = $('<a>').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 = $('<div>').addClass('previous')
+          .on('click', this.prev.bind(this))
+          .appendTo(this.$controls);
+        this.$playpause = $('<div>').addClass('play-pause')
+          .on('click', this.togglePlayPause.bind(this))
+          .appendTo(this.$controls);
+        this.$next = $('<div>').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();
+      // }
+    };
+
+
     //  ___             _         _   _
     // | _ \_ _ ___  __| |_  _ __| |_(_)___ _ _  ___
     // |  _/ '_/ _ \/ _` | || / _|  _| / _ \ ' \(_-<

+ 56 - 8
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;

+ 487 - 251
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,22 +44,243 @@
     // |___|\_/\___|_||_\__/__/
     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('search-results-loaded', initAjaxLinks)
+        .on('open_entree', closeAllModals)
+        .on('close_entree', backToFrontPage);
+    }
+
+    //  ___             _ _ ___
+    // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
+    // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
+    // |___/\__|_| \___/_|_|___/\__,_|_| /__/
+    function initScrollbars(){
+      // console.log("initScrollbars");
+      // TODO: find a better js scroll than overlayScrollbars which does not handle well max-height + overflow-y:auto;
+      // $('.os-scroll').overlayScrollbars({
+      //   overflowBehavior:{
+      //     x:'h',
+      //     y:'scroll',
+      //     clipAlways:false
+      //   }
+      // });
+    };
+
+    //    _    _
+    //   /_\  (_)__ ___ __
+    //  / _ \ | / _` \ \ /
+    // /_/ \_\/ \__,_/_\_\
+    //      |__/
+    // TODO: add url hash nav
+    // TODO: implement history.js
+    function initAjaxLinks(){
+      console.log('initAjaxLinks');
+
+      $('a', '#block-mainnavigation')
+        .add('a', '#block-footer.menu--footer')
+        .add('a', '#block-productions')
+        .add('a', 'article.node:not(.node--type-enregistrement) h2.node-title')
+        .add('a', '.productions-subtree')
+        .add('a', '.productions-parent')
+        // .add('a.index-link, a.notice-link', '#block-edlpentreesblock')
+        .addClass('ajax-link');
+
+
+      _$ajaxLinks = $('.ajax-link:not(.ajax-enabled)')
+        .each(function(i,e){
+          var $this = $(this);
+          // avoid already ajaxified links
+          if($this.is('.ajax-enable')) return;
+          if($this.attr('data-drupal-link-system-path')){
+            $this.on('click', onClickAjaxLink).addClass('ajax-enable');
+          }
+        });
+    };
+    function onClickAjaxLink(e){
+      e.preventDefault();
+      var $link = $(this);
+
+      if($link.is('.is-active'))
+        return false;
+
+      // Audio links
+      if($link.is('.audio-link')){
+        // TODO: stop randomplayer
+        _audioPlayer
+          .emmit('stop-shuffle')
+          .openDocument({
+            nid:$link.attr('nid'),
+            audio_url:$link.attr('audio_url')
+          });
+        return false;
+      }
+
+      // other links
+      var sys_path = $(this).attr('data-drupal-link-system-path');
+      var ajax_path = sys_path;
+      if(sys_path == '<front>'){
+        backToFrontPage();
+        return false;
+      }
+
+      // convert node link to edlp_ajax_node module links
+      var node_match = ajax_path.match(/^\/?(node\/\d+)$/g);
+      var term_match = ajax_path.match(/^\/?(taxonomy\/term\/\d+)$/g);
+      if(node_match){
+        ajax_path = 'edlp/ajax/json/'+node_match[0];
+        // check for viewmode attribute
+        if($link.attr('viewmode')){
+          ajax_path += '/'+$link.attr('viewmode');
+        }
+      }else if(term_match){
+        ajax_path = 'edlp/ajax/json/'+term_match[0];
+        ajax_path = ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
+        // check for viewmode attribute
+        if($link.attr('viewmode')){
+          ajax_path += '/'+$link.attr('viewmode');
+        }
+      }else{
+        // convert other link to ajax
+        ajax_path += '/ajax'
+      }
+
+      _$body.addClass('ajax-loading');
+      $link.addClass('ajax-loading');
+
+      // TODO: use Drupal.url()
+      // Drupal.url = function (path) {
+      //   return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
+      // };
+
+      var path = window.location.origin + Drupal.url(ajax_path);
+      $.getJSON(path, {})
+        .done(function(data){
+          onAjaxLinkLoaded(data, $link, sys_path);
         })
-        .on('close_entree', function(e){
-          // e.tid available
+        .fail(function(jqxhr, textStatus, error){
+          onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path);
+        });
+
+      return false;
+    };
+    function onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path){
+      console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
+      $link.removeClass('ajax-loading');
+      _$body.removeClass('ajax-loading');
+    };
+    function onAjaxLinkLoaded(data, $link, sys_path){
+      console.log('ajax link loaded : data', data);
+      _$body.removeClass('ajax-loading');
+
+      // replace all content with newly loaded
+      // TODO: build a system to replace or append contents (like studio + search)
+      _$row.html(data.rendered);
+
+      // add close btn
+      if(sys_path != 'procuction'){
+        addCloseBtnToCols();
+      }
+
+      // add body class for currently loaded content
+      var body_classes = [
+        'path-'+sys_path.replace(/\//g, '-'),
+        'entity-type-'+data.entity_type,
+        'bundle-'+data.bundle,
+        'view-mode-'+data.view_mode
+      ];
+      _$body.removeClass().addClass(body_classes.join(' '));
+
+      // id node add a generic path-node class to body
+      m = sys_path.match(/^\/?(node\/\d+)$/g);
+      if(m)
+        _$body.addClass('path-edlp-node');
+
+      // handle clicked link classes
+      _$ajaxLinks.removeClass('is-active');
+      $link.removeClass('ajax-loading').addClass('is-active');
+
+      // if block attached (eg : from edlp_productions module)
+      if(typeof data.block != 'undefined'){
+        // if block not already added
+        if(!$('#'+data.block.id, '.region-'+data.block.region).length){
+          $('.region-'+data.block.region).append(data.block.rendered);
+        }
+      }
+
+      initScrollbars();
+
+      if(sys_path == "productions")
+        initProductions();
+
+      initAjaxLinks();
+
+      _$body.trigger({'type':'new-content-ajax-loaded'});
+
+      // call behaviours
+      Drupal.attachBehaviors(_$row[0]);
+
+    };
+
+    function addCloseBtnToCols(){
+      $('.col', _$row).each(function(index, el) {
+
+        if($('span.close-col-btn', this).length)
+          return true;
+
+        $(this).children('.wrapper').append($('<span>')
+          .addClass('close-col-btn')
+          .on('click', function(e){
+            // check for theme attribute and emmit event
+            var $col = $(this).parents('.col');
+            var theme = $col.attr('theme');
+            if(theme != ''){
+              _$body.trigger({'type':theme+'-col-closed'});
+            }
+            // remove the col
+            $col.remove();
+            // if row is empty call closeAllModals()
+            if(!$('.col', _$row).length){
+              backToFrontPage();
+            }
+          })
+        );
+      });
+    };
+
+    //   ___
+    //  / __|___ _ _ _ __ _  _ ___
+    // | (__/ _ \ '_| '_ \ || (_-<
+    //  \___\___/_| | .__/\_,_/__/
+    //              |_|
+    function onCorpusMapReady(e){
+      console.log('theme : onCorpusReady', e);
+      _$corpus_canvas = $('canvas#corpus-map');
+      _$corpus_canvas
+        .on('corpus-cliked-on-map', function(e) {
+          console.log('theme : corpus-cliked-on-map');
           backToFrontPage();
+        })
+        .on('corpus-cliked-on-node', function(e) {
+          console.log('theme : corpus-cliked-on-node', e);
+          _audioPlayer
+            .emmit('stop-shuffle')
+            .openDocument(e.target_node);
         });
+
+      _randomPlayer = new RandomPlayer(e.playlist);
+
+      _$body.attr('corpus-map', 'ready');
     }
 
+
     //    _          _ _
     //   /_\ _  _ __| (_)___
     //  / _ \ || / _` | / _ \
@@ -105,6 +328,9 @@
 
       // object events
       this.event_handlers = {
+        'audio-open-document':[],
+        'audio-play':[],
+        'audio-pause':[],
         'audio-play-next':[],
         'audio-ended':[],
         'stop-shuffle':[]
@@ -135,11 +361,20 @@
         this.$next.on('click', this.playNext.bind(this));
         // TODO: previous and next btns
       },
-      openDocument(node){
-        console.log('AudioPlayer openDocument', node);
+      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(){
@@ -153,7 +388,7 @@
             'nid':this.historic[this.currentHistoricIndex].nid
           });
         } catch (e) {
-          console.warn('AudioPlayer : _$corpus_canvas does not exists');
+          console.info('AudioPlayer : _$corpus_canvas does not exists');
         }
 
         this.showHidePreviousBtn();
@@ -188,6 +423,7 @@
         this.play();
       },
       play(){
+        this.clearTimeOutToHide();
         this.audio.play();
       },
       playPrevious(){
@@ -212,16 +448,18 @@
         }
       },
       stop(){
-        console.log('AudioPlayer stop()');
+        // console.log('AudioPlayer stop()');
         this.audio.pause();
         this.timeOutToHide();
       },
       // audio events
       onPlaying(){
-        this.$container.addClass('is-playing');
+        this.$btns.addClass('is-playing');
+        this.emmit('audio-play');
       },
       onPause(){
-        this.$container.removeClass('is-playing');
+        this.$btns.removeClass('is-playing');
+        this.emmit('audio-pause');
       },
       onTimeupdate(){
         // move cursor
@@ -246,7 +484,7 @@
           .fail(this.onNodeLoadFail.bind(this));
       },
       onNodeLoaded(data){
-        console.log('AudioPlayer node loaded');
+        // console.log('AudioPlayer node loaded');
         this.$cartel.html(data.rendered).removeClass('loading');
         _$body.trigger({'type':'new-audio-cartel-loaded'});
         initAjaxLinks();
@@ -274,22 +512,26 @@
         }
       },
       timeOutToHide(){
-        console.log('AudioPlayer timeOutToHide()');
+        // console.log('AudioPlayer timeOutToHide()');
         this.clearTimeOutToHide();
         this.hideTimer = setTimeout(this.hide.bind(this), this.hideTimeMS);
       },
       clearTimeOutToHide(){
-        console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer);
+        // console.log('AudioPlayer clearTimeOutToHide()',this.hideTimer);
         if(this.hideTimer){
           clearTimeout(this.hideTimer);
           this.hideTimer = false;
         }
       },
       hide(){
-        console.log('AudioPlayer hide()');
+        // console.log('AudioPlayer hide()');
         this.$container.removeClass('visible');
         // trigger highlighted node remove on corpus map
-        _$corpus_canvas.trigger('audio-node-closed');
+        try {
+          _$corpus_canvas.trigger('audio-node-closed');
+        } catch (e) {
+          console.info('AudioPlayer hide() : _$corpus_canvas does not exists');
+        }
       },
       // object events
       on(event_name, handler){
@@ -299,238 +541,23 @@
         this.event_handlers[event_name].push(handler);
         return this;
       },
-      emmit(event_name){
+      emmit(event_name, args){
+        // console.log('AudioPlayer emmit() event_name', event_name);
+        // console.log('AudioPlayer emmit() handlers', this.event_handlers[event_name]);
         var handler;
-        for (var i = this.event_handlers[event_name].length; i >= 0 ; i--) {
+        var args  = args || {};
+        for (var i = this.event_handlers[event_name].length-1; i >= 0 ; i--) {
           handler = this.event_handlers[event_name][i];
-          setTimeout(handler, 0);
+          // console.log('AudioPlayer emmit() loop handler', handler);
+          setTimeout(function(){
+            // console.log('AudioPlayer emmit() timeout handler', handler);
+            handler(args);
+          }, 0);
         }
         return this;
       },
     }
 
-    //  ___             _ _ ___
-    // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
-    // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
-    // |___/\__|_| \___/_|_|___/\__,_|_| /__/
-    function initScrollbars(){
-      // console.log("initScrollbars");
-      // TODO: find a better js scroll than overlayScrollbars which does not handle well max-height + overflow-y:auto;
-      // $('.os-scroll').overlayScrollbars({
-      //   overflowBehavior:{
-      //     x:'h',
-      //     y:'scroll',
-      //     clipAlways:false
-      //   }
-      // });
-    };
-
-    //    _    _
-    //   /_\  (_)__ ___ __
-    //  / _ \ | / _` \ \ /
-    // /_/ \_\/ \__,_/_\_\
-    //      |__/
-    // TODO: add url hash nav
-    // TODO: implement history.js
-    function initAjaxLinks(){
-      console.log('initAjaxLinks');
-
-      $('a', '#block-mainnavigation')
-        .add('a', '#block-footer.menu--footer')
-        .add('a', '#block-productions')
-        .add('a', 'article.node:not(.node--type-enregistrement) h2.node-title')
-        .add('a', '.productions-subtree')
-        .add('a', '.productions-parent')
-        // .add('a.index-link, a.notice-link', '#block-edlpentreesblock')
-        .addClass('ajax-link');
-
-
-      _$ajaxLinks = $('.ajax-link:not(.ajax-enabled)')
-        .each(function(i,e){
-          var $this = $(this);
-          // avoid already ajaxified links
-          if($this.is('.ajax-enable')) return;
-          if($this.attr('data-drupal-link-system-path')){
-            $this.on('click', onClickAjaxLink).addClass('ajax-enable');
-          }
-        });
-    };
-    function onClickAjaxLink(e){
-      e.preventDefault();
-      var $link = $(this);
-
-      if($link.is('.is-active'))
-        return false;
-
-      // Audio links
-      if($link.is('.audio-link')){
-        // TODO: stop randomplayer
-        _audio_player
-          .emmit('stop-shuffle')
-          .openDocument({
-            nid:$link.attr('nid'),
-            audio_url:$link.attr('audio_url')
-          });
-        return false;
-      }
-
-      // other links
-      var sys_path = $(this).attr('data-drupal-link-system-path');
-      var ajax_path = sys_path;
-      if(sys_path == '<front>'){
-        backToFrontPage();
-        return false;
-      }
-
-      // convert node link to edlp_ajax_node module links
-      var node_match = ajax_path.match(/^\/?(node\/\d+)$/g);
-      var term_match = ajax_path.match(/^\/?(taxonomy\/term\/\d+)$/g);
-      if(node_match){
-        ajax_path = 'edlp/ajax/json/'+node_match[0];
-        // check for viewmode attribute
-        if($link.attr('viewmode')){
-          ajax_path += '/'+$link.attr('viewmode');
-        }
-      }else if(term_match){
-        ajax_path = 'edlp/ajax/json/'+term_match[0];
-        ajax_path = ajax_path.replace(/taxonomy\/term/, 'taxonomy_term');
-        // check for viewmode attribute
-        if($link.attr('viewmode')){
-          ajax_path += '/'+$link.attr('viewmode');
-        }
-      }else{
-        // convert other link to ajax
-        ajax_path += '/ajax'
-      }
-
-      _$body.addClass('ajax-loading');
-      $link.addClass('ajax-loading');
-
-      // TODO: use Drupal.url()
-      // Drupal.url = function (path) {
-      //   return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
-      // };
-
-      var path = window.location.origin + Drupal.url(ajax_path);
-      $.getJSON(path, {})
-        .done(function(data){
-          onAjaxLinkLoaded(data, $link, sys_path);
-        })
-        .fail(function(jqxhr, textStatus, error){
-          onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path);
-        });
-
-      return false;
-    };
-    function onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path){
-      console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
-      $link.removeClass('ajax-loading');
-      _$body.removeClass('ajax-loading');
-    };
-    function onAjaxLinkLoaded(data, $link, sys_path){
-      console.log('ajax link loaded : data', data);
-      _$body.removeClass('ajax-loading');
-
-      // replace all content with newly loaded
-      // TODO: build a system to replace or append contents (like studio + search)
-      _$row.html(data.rendered);
-
-      // add close btn
-      if(sys_path != 'procuction'){
-        addCloseBtnToCols();
-      }
-
-      // add body class for currently loaded content
-      var body_classes = [
-        'path-'+sys_path.replace(/\//g, '-'),
-        'entity-type-'+data.entity_type,
-        'bundle-'+data.bundle,
-        'view-mode-'+data.view_mode
-      ];
-      _$body.removeClass().addClass(body_classes.join(' '));
-
-      // id node add a generic path-node class to body
-      m = sys_path.match(/^\/?(node\/\d+)$/g);
-      if(m)
-        _$body.addClass('path-edlp-node');
-
-      // handle clicked link classes
-      _$ajaxLinks.removeClass('is-active');
-      $link.removeClass('ajax-loading').addClass('is-active');
-
-      // if block attached (eg : from edlp_productions module)
-      if(typeof data.block != 'undefined'){
-        // if block not already added
-        if(!$('#'+data.block.id, '.region-'+data.block.region).length){
-          $('.region-'+data.block.region).append(data.block.rendered);
-        }
-      }
-
-      initScrollbars();
-
-      if(sys_path == "productions")
-        initProductions();
-
-      initAjaxLinks();
-
-      _$body.trigger({'type':'new-content-ajax-loaded'});
-
-      // call behaviours
-      Drupal.attachBehaviors(_$row[0]);
-
-    };
-
-    function addCloseBtnToCols(){
-      $('.col', _$row).each(function(index, el) {
-
-        if($('span.close-col-btn', this).length)
-          return true;
-
-        $(this).children('.wrapper').append($('<span>')
-          .addClass('close-col-btn')
-          .on('click', function(e){
-            // check for theme attribute and emmit event
-            var $col = $(this).parents('.col');
-            var theme = $col.attr('theme');
-            if(theme != ''){
-              _$body.trigger({'type':theme+'-col-closed'});
-            }
-            // remove the col
-            $col.remove();
-            // if row is empty call closeAllModals()
-            if(!$('.col', _$row).length){
-              backToFrontPage();
-            }
-          })
-        );
-      });
-    };
-
-    //   ___
-    //  / __|___ _ _ _ __ _  _ ___
-    // | (__/ _ \ '_| '_ \ || (_-<
-    //  \___\___/_| | .__/\_,_/__/
-    //              |_|
-    function onCorpusMapReady(e){
-      console.log('theme : onCorpusReady', e);
-      _$corpus_canvas = $('canvas#corpus-map');
-      _$corpus_canvas
-        .on('corpus-cliked-on-map', function(e) {
-          console.log('theme : corpus-cliked-on-map');
-          backToFrontPage();
-        })
-        .on('corpus-cliked-on-node', function(e) {
-          console.log('theme : corpus-cliked-on-node', e);
-          _audio_player
-            .emmit('stop-shuffle')
-            .openDocument(e.target_node);
-        });
-
-      _randomPlayer = new RandomPlayer(e.playlist);
-
-      _$body.attr('corpus-map', 'ready');
-    }
-
     //  ___              _           ___ _
     // | _ \__ _ _ _  __| |___ _ __ | _ \ |__ _ _  _ ___ _ _
     // |   / _` | ' \/ _` / _ \ '  \|  _/ / _` | || / -_) '_|
@@ -540,7 +567,7 @@
       this.active = false;
       this.playlist = playlist;
       this.$btn = $('<a>').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 = $('<div>').addClass('previous')
+          .on('click', this.prev.bind(this))
+          .appendTo(this.$controls);
+        this.$playpause = $('<div>').addClass('play-pause')
+          .on('click', this.togglePlayPause.bind(this))
+          .appendTo(this.$controls);
+        this.$next = $('<div>').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();
+      // }
+    };
+
+
     //  ___             _         _   _
     // | _ \_ _ ___  __| |_  _ __| |_(_)___ _ _  ___
     // |  _/ '_/ _ \/ _` | || / _|  _| / _ \ ' \(_-<

+ 54 - 35
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{

+ 4 - 1
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']);
 }
 

+ 3 - 2
sites/all/themes/custom/edlptheme/templates/content/node--enregistrement--compo.html.twig

@@ -84,8 +84,9 @@
 <article{{ attributes.addClass(classes) }}>
   <h2{{ title_attributes.addClass('node-title') }}>
     <a href="{{ url }}" rel="bookmark" {{ link_attributes }}>
-      {% 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 #}
     </a>
   </h2>
 </article>

+ 9 - 2
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

+ 26 - 0
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

+ 10 - 0
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