Browse Source

player display first part of cartel

Bachir Soussi Chiadmi 6 years ago
parent
commit
608e9f6ae2

+ 2 - 1
sites/all/modules/figli/edlp_ajax_node/edlp_ajax_node.module

@@ -14,7 +14,8 @@ function edlp_ajax_node_theme($existing, $type, $theme, $path) {
     'edlp_ajax_node' => array(
       'file' => 'includes/edlp_ajax_node.inc',
       'variables' => array(
-        'node' => NULL
+        'node_object' => NULL,
+        'view_mode' => 'default'
       ),
     ),
   );

+ 4 - 2
sites/all/modules/figli/edlp_ajax_node/edlp_ajax_node.routing.yml

@@ -7,17 +7,19 @@
 # @License: GPL-V3
 
 edlp_ajax_node.node:
-  path: '/edlp/node/{nid}'
+  path: '/edlp/node/{nid}/{viewmode}'
   defaults:
     _controller: '\Drupal\edlp_ajax_node\Controller\EdlpAjaxNodeController::node'
     _title: 'Node'
+    viewmode: 'default'
   requirements:
     _permission: 'access content'
 
 edlp_ajax_node.ajaxnode:
-  path: '/edlp/node/{nid}/ajax'
+  path: '/edlp/node/{nid}/ajax/{viewmode}'
   defaults:
     _controller: '\Drupal\edlp_ajax_node\Controller\EdlpAjaxNodeController::nodejson'
     _title: 'AjaxNode'
+    viewmode: 'default'
   requirements:
     _permission: 'access content'

+ 1 - 1
sites/all/modules/figli/edlp_ajax_node/includes/edlp_ajax_node.inc

@@ -8,5 +8,5 @@ function template_preprocess_edlp_ajax_node(&$vars){
   @see https://www.drupal8.ovh/index.php/en/tutoriels/339/render-a-node-or-an-entity
   */
   $view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
-  $vars['node'] = $view_builder->view($vars['node'], 'default');
+  $vars['node'] = $view_builder->view($vars['node_object'], $vars['view_mode']);
 }

+ 20 - 10
sites/all/modules/figli/edlp_ajax_node/src/Controller/EdlpAjaxNodeController.php

@@ -14,11 +14,18 @@ class EdlpAjaxNodeController extends ControllerBase {
 
   private function toRenderable(){
     $this->query();
-    // dpm($this->future_nodes);
-    return array(
-      "#theme"=>'edlp_ajax_node',
-      '#node' => $this->node,
-    );
+
+    if($this->node){
+      return array(
+        "#theme"=>'edlp_ajax_node',
+        '#node_object' => $this->node,
+        '#view_mode' => $this->viewmode,
+      );
+    }else{
+      return array(
+        '#markup'=>'404! Node '.$this->nid." not found!"
+      );
+    }
   }
 
   /**
@@ -26,8 +33,9 @@ class EdlpAjaxNodeController extends ControllerBase {
    *
    * @return renderable array
    */
-  public function node($nid) {
+  public function node($nid, $viewmode) {
     $this->nid = $nid;
+    $this->viewmode = $viewmode;
     return $this->toRenderable();
   }
 
@@ -36,16 +44,18 @@ class EdlpAjaxNodeController extends ControllerBase {
    *
    * @return json
    */
-  public function nodejson($nid) {
+  public function nodejson($nid, $viewmode) {
     $this->nid = $nid;
-    $response = new JsonResponse();
+    $this->viewmode = $viewmode;
     $renderable = $this->toRenderable();
     $rendered = render($renderable);
 
+    $response = new JsonResponse();
     $response->setData([
-      'test'=>'hello edlp ajax node',
+      'test' => 'hello edlp ajax node',
       'nid' => $this->nid,
-      'rendered'=> $rendered
+      'view_mode' => $this->viewmode,
+      'rendered'=> $rendered,
     ]);
 
     return $response;

+ 3 - 2
sites/all/modules/figli/edlp_corpus/assets/dist/scripts/corpus.min.js

@@ -379,13 +379,14 @@
               var event = {
                 'type':'corpus-cliked-on-node',
                 'target_node':{
-                  'nid':_node_hover_id,
+                  'id':_node_hover_id,
+                  'nid':_nodes[_node_hover_id].nid,
                   'audio_url':_nodes[_node_hover_id].son_url
                 },
               };
               _$canvas.trigger(event);
             }else{
-              console.log('corpus : click on map');
+              // console.log('corpus : click on map');
               _$canvas.trigger('corpus-cliked-on-map');
             }
           }

+ 3 - 2
sites/all/modules/figli/edlp_corpus/assets/scripts/corpus.js

@@ -379,13 +379,14 @@
               var event = {
                 'type':'corpus-cliked-on-node',
                 'target_node':{
-                  'nid':_node_hover_id,
+                  'id':_node_hover_id,
+                  'nid':_nodes[_node_hover_id].nid,
                   'audio_url':_nodes[_node_hover_id].son_url
                 },
               };
               _$canvas.trigger(event);
             }else{
-              console.log('corpus : click on map');
+              // console.log('corpus : click on map');
               _$canvas.trigger('corpus-cliked-on-map');
             }
           }

+ 1 - 3
sites/all/modules/figli/edlp_corpus/src/Controller/CorpusController.php

@@ -41,11 +41,9 @@ class CorpusController extends ControllerBase {
 
     $nodes_data = [];
     foreach ($nodes as $node) {
-
-      // this is ideal but it's too heavy to load : the whole ajax json goes from 138kb to 1.23Md (even optimized)...
+      // this would be ideal but it's too heavy to load : the whole ajax json goes from 138kb to 1.23Md (even optimized)...
       // $node_builder = $view_builder->view($node, 'popup');
 
-
       $entrees = [];
       foreach ($node->get('field_entrees')->getValue() as $key => $term) {
         $entrees[] = $term['target_id'];

+ 32 - 3
sites/all/themes/custom/edlptheme/assets/dist/scripts/main.min.js

@@ -81,6 +81,10 @@ edlp_vars = {
       this.$time        = $('<div>').addClass('time').appendTo(this.$container);
       this.$currentTime = $('<div>').addClass('current-time').html('00:00').appendTo(this.$time);
       this.$duration    = $('<div>').addClass('duration').html('00:00').appendTo(this.$time);
+      // favoris
+      this.$fav       = $('<div>').addClass('favoris').appendTo(this.$container);
+      // cartel
+      this.$cartel       = $('<div>').addClass('cartel').appendTo(this.$container);
 
       // hiding
       this.hideTimer = false;
@@ -109,8 +113,28 @@ edlp_vars = {
         this.$playpause.on('click', this.togglePlayPause.bind(this));
         // TODO: previous and next btns
       },
+      openDocument(node){
+        this.setSRC(node.audio_url);
+        this.loadNode(node.nid);
+      },
+      // cartel functions
+      loadNode(nid){
+        this.$cartel.addClass('loading');
+        $.getJSON('/edlp/node/'+nid+'/ajax/player_cartel', {})
+          .done(this.onNodeLoaded.bind(this))
+          .fail(this.onNodeLoadFail.bind(this));
+      },
+      onNodeLoaded(data){
+        console.log('AudioPlayer node loaded', data);
+        this.$cartel.html(data.rendered);
+        this.$cartel.removeClass('loading');
+      },
+      onNodeLoadFail(jqxhr, textStatus, error){
+        console.warn('AudioPlayer node load failed', jqxhr.responseText);
+      },
+      // audio functions
       setSRC(url){
-        console.log('AudioPlayer setSRC : url', url);
+        // console.log('AudioPlayer setSRC : url', url);
         this.clearTimeOutToHide();
         this.audio.src = url;
         this.show();
@@ -168,6 +192,7 @@ edlp_vars = {
         this.$container.removeClass('is-playing');
         this.timeOutToHide();
       },
+      // global
       show(){
         this.$container.addClass('visible');
       },
@@ -182,8 +207,12 @@ edlp_vars = {
       },
       hide(){
         this.$container.removeClass('visible');
+        // TODO: trigger highlighted node remove on corpus map
       }
     }
+
+
+
     //  ___             _ _ ___
     // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
     // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
@@ -256,7 +285,7 @@ edlp_vars = {
     };
 
     function onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path){
-      console.warn('ajaxlink load failed', jqxhr.responseText);
+      console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
       $link.removeClass('ajax-loading');
       _$body.removeClass('ajax-loading');
     };
@@ -311,7 +340,7 @@ edlp_vars = {
         })
         .on('corpus-cliked-on-node', function(e) {
           console.log('theme : corpus-cliked-on-node', e);
-          _audio_player.setSRC(e.target_node.audio_url);
+          _audio_player.openDocument(e.target_node);
         });
     }
 

+ 71 - 6
sites/all/themes/custom/edlptheme/assets/dist/styles/app.min.css

@@ -1099,7 +1099,7 @@ div.layout-container {
   position: relative;
   width: 100%;
   height: 100%;
-  padding: 1em;
+  padding: 0.5em 1em;
   -webkit-box-sizing: border-box;
   box-sizing: border-box;
   z-index: 1; }
@@ -1135,9 +1135,10 @@ footer[role="contentinfo"] {
 
 header[role="banner"] {
   position: relative;
-  padding: 0 0 1em 0;
+  padding: 0 0 0.5em 0;
   border-bottom: 1px solid red;
-  pointer-events: all; }
+  pointer-events: all;
+  height: 70px; }
 
 #block-edlptheme-branding {
   display: inline-block; }
@@ -1297,7 +1298,7 @@ body.ajax-loading main[role="main"]:before {
   left: 0;
   background-color: white;
   height: 100%;
-  width: 300px;
+  min-width: 300px;
   z-index: 20;
   opacity: 0;
   pointer-events: none;
@@ -1306,10 +1307,11 @@ body.ajax-loading main[role="main"]:before {
   #audio-player.visible {
     opacity: 1;
     pointer-events: all; }
-  #audio-player .btns, #audio-player .time-line-container, #audio-player .time {
+  #audio-player > * {
     display: inline-block;
     vertical-align: middle;
-    padding: 0 1em 0 0; }
+    padding: 0;
+    max-height: 100%; }
   #audio-player .btns {
     cursor: pointer; }
     #audio-player .btns > * {
@@ -1358,6 +1360,69 @@ body.ajax-loading main[role="main"]:before {
   #audio-player .time .duration {
     font-size: 0.75em;
     font-weight: 400; }
+  #audio-player .favoris {
+    height: 100%; }
+  #audio-player .cartel {
+    max-width: 400px;
+    margin-left: 1em;
+    background-color: white;
+    opacity: 1;
+    -webkit-transition: opacity 0.5s ease-in-out;
+    transition: opacity 0.5s ease-in-out; }
+    #audio-player .cartel.loading {
+      opacity: 0; }
+    #audio-player .cartel .entrees {
+      line-height: 0; }
+      #audio-player .cartel .entrees span {
+        display: inline-block;
+        width: 8px;
+        height: 8px;
+        background-color: black;
+        margin-right: 3px; }
+        #audio-player .cartel .entrees span[tid='134'] {
+          background-color: #2b8f2f; }
+        #audio-player .cartel .entrees span[tid='121'] {
+          background-color: #3a33b6; }
+        #audio-player .cartel .entrees span[tid='125'] {
+          background-color: #2c9f57; }
+        #audio-player .cartel .entrees span[tid='119'] {
+          background-color: #c48978; }
+        #audio-player .cartel .entrees span[tid='132'] {
+          background-color: #5270bb; }
+        #audio-player .cartel .entrees span[tid='122'] {
+          background-color: #fb54d3; }
+        #audio-player .cartel .entrees span[tid='129'] {
+          background-color: #e07483; }
+        #audio-player .cartel .entrees span[tid='120'] {
+          background-color: #655845; }
+        #audio-player .cartel .entrees span[tid='130'] {
+          background-color: #7e0868; }
+        #audio-player .cartel .entrees span[tid='118'] {
+          background-color: #0e7121; }
+        #audio-player .cartel .entrees span[tid='127'] {
+          background-color: #dabd42; }
+        #audio-player .cartel .entrees span[tid='133'] {
+          background-color: #0399bb; }
+        #audio-player .cartel .entrees span[tid='128'] {
+          background-color: #399a1c; }
+        #audio-player .cartel .entrees span[tid='124'] {
+          background-color: #708540; }
+        #audio-player .cartel .entrees span[tid='116'] {
+          background-color: #191bff; }
+        #audio-player .cartel .entrees span[tid='117'] {
+          background-color: #279d84; }
+        #audio-player .cartel .entrees span[tid='131'] {
+          background-color: #5219ab; }
+        #audio-player .cartel .entrees span[tid='126'] {
+          background-color: #d49cb6; }
+        #audio-player .cartel .entrees span[tid='123'] {
+          background-color: #497715; }
+    #audio-player .cartel h2.node-title {
+      margin: 0.2em 0 0;
+      font-size: 1em; }
+    #audio-player .cartel p {
+      margin: 0;
+      font-size: 0.75em; }
   #audio-player.is-playing .btns .play-pause {
     background-image: url(../img/audio-player-pause.svg); }
 

+ 32 - 3
sites/all/themes/custom/edlptheme/assets/scripts/main.js

@@ -58,6 +58,10 @@
       this.$time        = $('<div>').addClass('time').appendTo(this.$container);
       this.$currentTime = $('<div>').addClass('current-time').html('00:00').appendTo(this.$time);
       this.$duration    = $('<div>').addClass('duration').html('00:00').appendTo(this.$time);
+      // favoris
+      this.$fav       = $('<div>').addClass('favoris').appendTo(this.$container);
+      // cartel
+      this.$cartel       = $('<div>').addClass('cartel').appendTo(this.$container);
 
       // hiding
       this.hideTimer = false;
@@ -86,8 +90,28 @@
         this.$playpause.on('click', this.togglePlayPause.bind(this));
         // TODO: previous and next btns
       },
+      openDocument(node){
+        this.setSRC(node.audio_url);
+        this.loadNode(node.nid);
+      },
+      // cartel functions
+      loadNode(nid){
+        this.$cartel.addClass('loading');
+        $.getJSON('/edlp/node/'+nid+'/ajax/player_cartel', {})
+          .done(this.onNodeLoaded.bind(this))
+          .fail(this.onNodeLoadFail.bind(this));
+      },
+      onNodeLoaded(data){
+        console.log('AudioPlayer node loaded', data);
+        this.$cartel.html(data.rendered);
+        this.$cartel.removeClass('loading');
+      },
+      onNodeLoadFail(jqxhr, textStatus, error){
+        console.warn('AudioPlayer node load failed', jqxhr.responseText);
+      },
+      // audio functions
       setSRC(url){
-        console.log('AudioPlayer setSRC : url', url);
+        // console.log('AudioPlayer setSRC : url', url);
         this.clearTimeOutToHide();
         this.audio.src = url;
         this.show();
@@ -145,6 +169,7 @@
         this.$container.removeClass('is-playing');
         this.timeOutToHide();
       },
+      // global
       show(){
         this.$container.addClass('visible');
       },
@@ -159,8 +184,12 @@
       },
       hide(){
         this.$container.removeClass('visible');
+        // TODO: trigger highlighted node remove on corpus map
       }
     }
+
+
+
     //  ___             _ _ ___
     // / __| __ _ _ ___| | | _ ) __ _ _ _ ___
     // \__ \/ _| '_/ _ \ | | _ \/ _` | '_(_-<
@@ -233,7 +262,7 @@
     };
 
     function onAjaxLinkLoadError(jqxhr, textStatus, error, $link, sys_path){
-      console.warn('ajaxlink load failed', jqxhr.responseText);
+      console.warn('ajaxlink load failed for '+sys_path+' : '+error, jqxhr.responseText);
       $link.removeClass('ajax-loading');
       _$body.removeClass('ajax-loading');
     };
@@ -288,7 +317,7 @@
         })
         .on('corpus-cliked-on-node', function(e) {
           console.log('theme : corpus-cliked-on-node', e);
-          _audio_player.setSRC(e.target_node.audio_url);
+          _audio_player.openDocument(e.target_node);
         });
     }
 

+ 73 - 41
sites/all/themes/custom/edlptheme/assets/styles/app.scss

@@ -13,6 +13,43 @@
 @import 'base/layout';
 @import 'base/fonts';
 
+@mixin spining-loader-square{
+  @keyframes rotation {
+    from {transform: rotate(0deg);}
+    to   {transform: rotate(359deg);}
+  }
+  animation: rotation 2s infinite linear;
+}
+
+
+@mixin entrie-micro-square {
+  display:inline-block;
+  $s:8px;
+  width:$s; height:$s;
+  background-color: black;
+  margin-right: 3px;
+  &[tid='134']{background-color: $e_col_134;}
+  &[tid='121']{background-color: $e_col_121;}
+  &[tid='125']{background-color: $e_col_125;}
+  &[tid='119']{background-color: $e_col_119;}
+  &[tid='132']{background-color: $e_col_132;}
+  &[tid='122']{background-color: $e_col_122;}
+  &[tid='129']{background-color: $e_col_129;}
+  &[tid='120']{background-color: $e_col_120;}
+  &[tid='130']{background-color: $e_col_130;}
+  &[tid='118']{background-color: $e_col_118;}
+  &[tid='127']{background-color: $e_col_127;}
+  &[tid='133']{background-color: $e_col_133;}
+  &[tid='128']{background-color: $e_col_128;}
+  &[tid='124']{background-color: $e_col_124;}
+  &[tid='116']{background-color: $e_col_116;}
+  &[tid='117']{background-color: $e_col_117;}
+  &[tid='131']{background-color: $e_col_131;}
+  &[tid='126']{background-color: $e_col_126;}
+  &[tid='123']{background-color: $e_col_123;}
+}
+
+
 // header
 
 .layout-container{
@@ -21,17 +58,11 @@
 
 header[role="banner"]{
   position: relative;
-  padding:0 0 1em 0;
+  padding:0 0 0.5em 0;
   border-bottom: 1px solid red;
   pointer-events: all;
-}
-
-@mixin spining-loader-square{
-  @keyframes rotation {
-    from {transform: rotate(0deg);}
-    to   {transform: rotate(359deg);}
-  }
-  animation: rotation 2s infinite linear;
+  // TODO: what header height to fit well with player ??
+  height:70px;
 }
 
 #block-edlptheme-branding{
@@ -192,19 +223,22 @@ main[role="main"]{
   position: absolute;
   top:0; left:0;
   background-color: white;
-  height:100%; width:300px;
+  height:100%; min-width:300px;
   z-index: 20;
   opacity: 0;
+  // outline: 1px solid blue;
   pointer-events: none;
   transition: opacity 0.7s ease-in-out;
   &.visible{
     opacity: 1;
     pointer-events:all;
   }
-  .btns, .time-line-container, .time{
+  &>*{
     display: inline-block;
     vertical-align: middle;
-    padding:0 1em 0 0;
+    padding:0;
+    max-height: 100%;
+    // outline: 1px solid green;
   }
   .btns{
     // outline: 1px dotted orange;
@@ -227,13 +261,11 @@ main[role="main"]{
       opacity: 0.3;}
   }
   .time-line-container{
-    // outline: 1px dotted purple;
     .time-line{
       position: relative;
       width:70px; height:1px;
       background-color: #000;
       overflow: visible;
-      // border-top: 1px solid #000;
       transform: rotateZ(-45deg);
       .loader{
         width:0; height:100%;
@@ -249,11 +281,9 @@ main[role="main"]{
     }
   }
   .time{
-    // outline: 1px dotted brown;
     &>*{
       width:70px;
       text-align: right;
-      // outline: 1px dotted red;
     }
     .current-time{
       font-size: 1.4em;
@@ -264,7 +294,33 @@ main[role="main"]{
       font-weight: 400;
     }
   }
+  .favoris{
+    height:100%;
+  }
+  .cartel{
+    // TODO: set max-width regarding responsive
+    max-width: 400px;
+    margin-left: 1em;
+    background-color: white;
+    opacity: 1;
+    transition: opacity 0.5s ease-in-out;
+    &.loading{opacity: 0;}
 
+    .entrees{
+      line-height: 0;
+      span{
+        @include entrie-micro-square;
+      }
+    }
+    h2.node-title{
+      margin:0.2em 0 0;
+      font-size: 1em;
+    }
+    p{
+      margin:0;
+      font-size: 0.75em;
+    }
+  }
   &.is-playing{
     .btns .play-pause{background-image: url(../img/audio-player-pause.svg);}
   }
@@ -726,7 +782,6 @@ footer{
 
 }
 
-
 //  _  _         _       ___
 // | \| |___  __| |___  | _ \___ _ __ _  _ _ __
 // | .` / _ \/ _` / -_) |  _/ _ \ '_ \ || | '_ \
@@ -736,30 +791,7 @@ footer{
   .inner{
     .entrees{
       span{
-        display:inline-block;
-        $s:8px;
-        width:$s; height:$s;
-        background-color: black;
-        margin-right: 3px;
-        &[tid='134']{background-color: $e_col_134;}
-        &[tid='121']{background-color: $e_col_121;}
-        &[tid='125']{background-color: $e_col_125;}
-        &[tid='119']{background-color: $e_col_119;}
-        &[tid='132']{background-color: $e_col_132;}
-        &[tid='122']{background-color: $e_col_122;}
-        &[tid='129']{background-color: $e_col_129;}
-        &[tid='120']{background-color: $e_col_120;}
-        &[tid='130']{background-color: $e_col_130;}
-        &[tid='118']{background-color: $e_col_118;}
-        &[tid='127']{background-color: $e_col_127;}
-        &[tid='133']{background-color: $e_col_133;}
-        &[tid='128']{background-color: $e_col_128;}
-        &[tid='124']{background-color: $e_col_124;}
-        &[tid='116']{background-color: $e_col_116;}
-        &[tid='117']{background-color: $e_col_117;}
-        &[tid='131']{background-color: $e_col_131;}
-        &[tid='126']{background-color: $e_col_126;}
-        &[tid='123']{background-color: $e_col_123;}
+        @include entrie-micro-square;
       }
     }
     .title{

+ 1 - 1
sites/all/themes/custom/edlptheme/assets/styles/base/_layout.scss

@@ -13,7 +13,7 @@ body, html{
 div.layout-container{
   position: relative;
   width:100%; height:100%;
-  padding:1em;
+  padding:0.5em 1em;
   box-sizing: border-box;
   z-index: 1;
 }

+ 0 - 0
sites/all/themes/custom/edlptheme/templates/content/edlp-ajax-node.html.twig → sites/all/themes/custom/edlptheme/templates/content/_BAD_edlp-ajax-node.html.twig


+ 100 - 0
sites/all/themes/custom/edlptheme/templates/content/node--player-cartel.html.twig

@@ -0,0 +1,100 @@
+{#
+/**
+ * @file
+ * Theme override to display a node.
+ *
+ * Available variables:
+ * - node: The node entity with limited access to object properties and methods.
+ *   Only method names starting with "get", "has", or "is" and a few common
+ *   methods such as "id", "label", and "bundle" are available. For example:
+ *   - node.getCreatedTime() will return the node creation timestamp.
+ *   - node.hasField('field_example') returns TRUE if the node bundle includes
+ *     field_example. (This does not indicate the presence of a value in this
+ *     field.)
+ *   - node.isPublished() will return whether the node is published or not.
+ *   Calling other methods, such as node.delete(), will result in an exception.
+ *   See \Drupal\node\Entity\Node for a full list of public properties and
+ *   methods for the node object.
+ * - label: The title of the node.
+ * - content: All node items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given child element.
+ * - author_picture: The node author user entity, rendered using the "compact"
+ *   view mode.
+ * - metadata: Metadata for this node.
+ * - date: Themed creation date field.
+ * - author_name: Themed author name field.
+ * - url: Direct URL of the current node.
+ * - display_submitted: Whether submission information should be displayed.
+ * - attributes: HTML attributes for the containing element.
+ *   The attributes.class element may contain one or more of the following
+ *   classes:
+ *   - node: The current template type (also known as a "theming hook").
+ *   - node--type-[type]: The current node type. For example, if the node is an
+ *     "Article" it would result in "node--type-article". Note that the machine
+ *     name will often be in a short form of the human readable label.
+ *   - node--view-mode-[view_mode]: The View Mode of the node; for example, a
+ *     teaser would result in: "node--view-mode-teaser", and
+ *     full: "node--view-mode-full".
+ *   The following are controlled through the node publishing options.
+ *   - node--promoted: Appears on nodes promoted to the front page.
+ *   - node--sticky: Appears on nodes ordered above other non-sticky nodes in
+ *     teaser listings.
+ *   - node--unpublished: Appears on unpublished nodes visible only to site
+ *     admins.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - content_attributes: Same as attributes, except applied to the main
+ *   content tag that appears in the template.
+ * - author_attributes: Same as attributes, except applied to the author of
+ *   the node tag that appears in the template.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
+ * - page: Flag for the full page state. Will be true if view_mode is 'full'.
+ * - readmore: Flag for more state. Will be true if the teaser content of the
+ *   node cannot hold the main body content.
+ * - logged_in: Flag for authenticated user status. Will be true when the
+ *   current user is a logged-in member.
+ * - is_admin: Flag for admin user status. Will be true when the current user
+ *   is an administrator.
+ *
+ * @see template_preprocess_node()
+ *
+ * @todo Remove the id attribute (or make it a class), because if that gets
+ *   rendered twice on a page this is invalid CSS for example: two lists
+ *   in different view modes.
+ */
+#}
+{%
+  set classes = [
+    'node',
+    'node--type-' ~ node.bundle|clean_class,
+    node.isPromoted() ? 'node--promoted',
+    node.isSticky() ? 'node--sticky',
+    not node.isPublished() ? 'node--unpublished',
+    view_mode ? 'node--view-mode-' ~ view_mode|clean_class,
+  ]
+%}
+{{ attach_library('classy/node') }}
+<article{{ attributes.addClass(classes) }}>
+  <div class="entrees">
+    {# THIS IS REALLY DIRTY !! #}
+    {% for key, child in content.field_entrees if key|first != '#' %}
+      {% set tid = child['#cache']['tags'][0]|replace({'taxonomy_term:':''}) %}
+      <span class="entree" tid="{{ tid }}" title="{{ child }}"></span>
+    {% endfor %}
+  </div>
+  <h2{{ title_attributes.addClass('node-title') }}>
+    {{ label }}
+  </h2>
+
+  <div{{ content_attributes.addClass('node__content') }}>
+    {{ content|without('field_entrees') }}
+  </div>
+
+</article>