regenerate: true


{% capture cache %}

{% comment %}

# -----------------------------------------------------------------------------
#  ~/assets/themes/j1/core/js/adapter/template.js
#  Liquid template to create the Template Adapter for J1 Template Core
#
#  Product/Info:
#  https://jekyll.one
#
#  Copyright (C) 2019 Juergen Adams
#
#  J1 Template is licensed under the MIT License.
#  For details, see https://jekyll.one
#
# -----------------------------------------------------------------------------
#  TODO: jadams, 2019-01-20: Isolate drawer, switch from BME code
# -----------------------------------------------------------------------------
# Test data:
#  {{ liquid_var | debug }}
#
# -----------------------------------------------------------------------------

{% endcomment %}

/*

# -----------------------------------------------------------------------------
#  ~/assets/themes/j1/core/js/adapter/j1_template.js
#  Main JS Adapter for J1 Template
#
#  Product/Info:
#  https://jekyll.one
#
#  Copyright (C) 2019 Juergen Adams
#
#  J1 Template is licensed under the MIT License.
#  For details, see https://jekyll.one
#
# -----------------------------------------------------------------------------
#  Adapter generated: {{site.time}}
# -----------------------------------------------------------------------------

*/ 'use strict';

{% comment %} Variables ——————————————————————————– {% endcomment %} {% if site.permalink == 'none' %}

{% capture page_url %}{{ site.url }}.html{% endcapture %}

{% else %}

{% capture page_url %}{{ site.url }}{% endcapture %}

{% endif %}

{% assign environment = site.environment %} {% assign template_version = site.version %} {% assign hideOnReload = site.data.modules.j1_theme_switcher.hideOnReload %}

{% assign modules = site.data.modules %} {% assign banner_config = modules.j1_banner.banners %} {% assign panel_config = modules.j1_panel.panels %} {% assign toccer_config = modules.j1_toccer %} {% assign footer_config = modules.j1_footer %} {% assign footer_id = modules.j1_footer.global.id %}

{% assign banner_data_path = modules.j1_banner.global.data_path %} {% assign panel_data_path = modules.j1_panel.global.data_path %} {% assign footer_data_path = modules.j1_footer.global.data_path %}

{% comment %} Main ——————————————————————————– {% endcomment %} var j1 = function () {

var environment         = '{{environment}}'; // Set environment

// Status information
var state = 'not_started';
var current_page;
var previous_page;
var app_detected;
var user_state_detected;

// Theme information
var themeName;
var themeCss;
var themeExtensionCss;

// J1 data files
var j1_colors           = {};
var colors_data_path    = "/assets/data/colors.json";

// Logger
var logText;

// J1 UserState Cookie (initial values)
var user_state = {
  'theme_css' : '/assets/themes/j1/core/css/uno.css',
  'theme_extension_css' : '/assets/themes/j1/core/css/theme_extensions.css',
  'theme_name' : 'Uno',
  'theme_author' : 'J1 Team',
  'theme_link' : 'https://jekyll.one/',
  'provider' : '{{site.data.j1_config.user.provider}}',
  'provider_membership' : '{{site.data.j1_config.user.provider_membership}}',
  "provider_permissions" : "{{site.data.j1_config.user.provider_permissions}}",
  "provider_url" : "{{site.data.j1_config.user.provider_url}}", 
  'status' : 'pending',
  'user_name' : '{{site.data.j1_config.user.user_name}}',
  'language' : 'unknown',
  'cookies_accepted' : "pending",
  'previous_page' : "/",
  'last_pager' : "no_pager",
  'writer' : 'web'
};

return {

  // Initialize
  init: function ( options ) {
    var re = new RegExp("navigator|dateview|tagview|archive");
    var last_pager;
    var last_pager_url;
    var logger;

    logger = log4javascript.getLogger('j1.Template.init');
    state = 'started';
    logger.info('state: ' + state); // Set|Log status

    if ( options  !== undefined ) {
      var settings = $.extend({}, options);
    } else {
      var settings = false;
    }

    if ( settings.scrollbar === 'false'  ) {
      $('body').addClass( 'hide-scrollbar' )
      $('html').addClass( 'hide-scrollbar-moz' )
    }

    // Detect|Set J1 AppSession
    app_detected = j1.existsCookie ( 'j1.web.session' );

    // Detect|Set J1 UserState
    user_state_detected = j1.existsCookie ( 'j1.user.state' );
    if ( user_state_detected ) {
      logger.info('Read UserState cookie');
      user_state        = j1.getUserStateCookie();
      themeName         = user_state.theme_name;
      themeCss          = user_state.theme_css;
      themeExtensionCss = user_state.theme_extension_css;
    } else {
      logger.warn('UserState cookie NOT found'); 
    }

    // Save last page access
    current_page              = window.location.pathname;
    previous_page             = user_state.previous_page;
    user_state.previous_page  = current_page;

    // Calculate last "pager" if any
    if (re.test(user_state.previous_page)) {
      last_pager = user_state.previous_page;
      user_state.last_pager = last_pager;
    } else {
      last_pager = user_state.last_pager;
    }
    j1.setUserStateCookie( user_state );

    {% comment %} Deferred data load
    -------------------------------------------------------------------------- {% endcomment %}
    // Load banner and panel HTML data asychronously
    // See: https://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-requests-are-done
    $.when( j1.initBanner(settings), j1.initPanel(settings), j1.initFooter( settings ) ).done (
      function(initBanner_response, initPanel_response) {
        var logger = log4javascript.getLogger('j1.Template.adapter');

        state = 'running';
        logger.info('initBanner: state: ' + state);

        j1.setUserStateCookie( user_state );
        // jadams, 2019-01-20: enabled only drawer, switch and ripples,
        // initialize BMD JS components
        j1.initMDB(settings);
        j1.initClipboard(settings);
        j1.displayPage(settings);
    });

    return true;
  }, // END init

  // -------------------------------------------------------------------------
  // AJAX loader to load and place all banner used for a page
  // -------------------------------------------------------------------------
  // ToDo:
  initBanner: function ( options ) {
    var banner        = [];
    var bannerOptions = [];
    var logger;

    logger  = log4javascript.getLogger('j1.Template.initBanner');
    logText = 'initBanner: Start initialization';
    logger.info(logText);

    {% comment %} helper functions for (AJAX) data load
    -------------------------------------------------------------------------- {% endcomment %}
    function load_color_data() {
      // Returns the j1_colors object
      return $.ajax({
          url:      colors_data_path,
          success:  function (data) {
            if (typeof data == 'string') {
              j1_colors = JSON.parse(data);
            }
            if (typeof data == 'object') {
              j1_colors = data;
            }
          }
      });
    };

    // closure to pass additional data (e.g. div #id)
    // to AJAX load callback (panel_id)
    // See: http://stackoverflow.com/questions/939032/jquery-pass-more-parameters-into-callback
    var cb_load_closure = function(panel_id) {
      return function ( responseTxt, statusTxt, xhr ) {
        if ( statusTxt ==  'success' ) {
          logText = 'initBanner: Banner on ID ' +panel_id+ ' loaded successfully';
          logger.info(logText);
        }
        if ( statusTxt == 'error' ) {
          logText = 'initBanner: Banner on ID ' +panel_id+ ' loading failed. Error: ' + xhr.status + ': ' + xhr.statusText;
          logger.error(logText);
          // Set|Log status
          state = 'failed';
          logger.error('state: ' + state);
        }
      };
    };

    {% comment %} Create CSS styles
    -------------------------------------------------------------------------- {% endcomment %}
    // Load color and font (json) data asychronously
    // See: https://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-requests-are-done
    $.when( load_color_data() ).done (
      function( color_data ) {
        // Collect all banner id|s configured
        {% for items in banner_config %}
          {% for banners in items %}
            {% for banner in banners %}
                {% for item in banner %}
                  {% assign key = item[0] %}
                  {% assign value = item[1] %}
                  {% if key == 'id' %} {% assign id = value %} {% endif %}
                  {% if key == 'background_color_1' %} {% assign background_color_1 = value %} {% endif %}
                  {% if key == 'background_color_2' %} {% assign background_color_2 = value %} {% endif %}
                {% endfor %}
            {% endfor %}

            {% comment %} Add header CSS styles to <HEAD>
            ------------------------------------------------------------------ {% endcomment %}
            var banner_backround_style = '';
            // Initialze banner background gradient/colors
            banner_backround_style += "<style> .{{id}}-background { ";
            banner_backround_style += "background-image: -webkit-gradient(linear, left top, left bottom, from( " +color_data['{{background_color_1}}']+ " ), to(  " +color_data['{{background_color_2}}']+ " ));";
            banner_backround_style += "background-image: -webkit-linear-gradient(top, " +color_data['{{background_color_1}}']+ " 0%, " +color_data['{{background_color_2}}']+ " 100%);";
            banner_backround_style += "background-image: -o-linear-gradient(top, " +color_data['{{background_color_1}}']+ " 0%, " +color_data['{{background_color_2}}']+ " 100%);";
            banner_backround_style += "background-image: linear-gradient(to bottom, " +color_data['{{background_color_1}}']+ " 0%, " +color_data['{{background_color_2}}']+ " 100%);";
            banner_backround_style += 'filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="' +color_data['{{background_color_1}}']+ '", endColorstr="' +color_data['{{background_color_2}}']+ '", GradientType=0); ';
            banner_backround_style += "} </style>";
            $('head').append( banner_backround_style );
          {% endfor %}
        {% endfor %}
    });

    {% comment %} Collect all banner id|s configured
    -------------------------------------------------------------------------- {% endcomment %}
    {% for items in banner_config %}
      {% for banners in items %}
        {% for banner in banners %}
            {% for item in banner %}
              {% assign key = item[0] %}
              {% assign value = item[1] %}
              {% if key == 'id' %} {% assign id = value %} {% endif %}
              {% if key == 'background_color_1' %} {% assign background_color_1 = value %} {% endif %}
              {% if key == 'background_color_2' %} {% assign background_color_2 = value %} {% endif %}
            {% endfor %}
        {% endfor %}
        banner.push('{{id}}');
      {% endfor %}
    {% endfor %}

    if ( banner.length ) {
      logText = 'initBanner Banner are being loaded deferred (if any)';
      logger.info(logText);
    }

    for (var i in banner) {
      var id = '#' + banner[i];
      var selector = $(id);
      if ( selector.length ) {
        var banner_data_path = '{{banner_data_path}} ' + id + ' > *';
        selector.load( banner_data_path, cb_load_closure( id ) );
      }
    }

    return true;
  }, // END initBanner

  // -------------------------------------------------------------------------
  // AJAX loader for panel used with J1 Template fo all pages
  // -------------------------------------------------------------------------
  // ToDo:
  initPanel: function ( options ) {    
    var panel = [];
    var logger;

    logger = log4javascript.getLogger('j1.Template.initPanel');
    logText = 'initPanel: Start initialization';
    logger.info(logText);

    // closure to pass additional data (e.g. div #id)
    // to AJAX load callback (panel_id)
    // See: http://stackoverflow.com/questions/939032/jquery-pass-more-parameters-into-callback
    var cb_load_closure = function(panel_id) {
      return function ( responseTxt, statusTxt, xhr ) {
        if ( statusTxt == 'success' ) {
          logText = 'initPanel: Panel on ID ' +panel_id+ ' loaded successfully';
          logger.info(logText);
        }
        if ( statusTxt == 'error' ) {
          logText = 'initPanel: Panel on ID ' +panel_id+ ' loading failed. Error: ' + xhr.status + ': ' + xhr.statusText;
          logger.error(logText);
          // Set|Log status
          state = 'Error';
          logger.error('state: ' + state);
        }
      };
    };

    // Collect all panel id|s configured
    {% for items in panel_config %}
      {% for panels in items %}
        {% for panel in panels %}
            {% for item in panel %}
              {% assign key = item[0] %}
              {% assign value = item[1] %}
              {% if key == 'id' %} {% assign id = value %} {% endif %}
            {% endfor %}
        {% endfor %}
        panel.push('{{id}}');
      {% endfor %}
    {% endfor %}

    if ( panel.length ) {
      logText = 'initPanel: Panel are being loaded deferred (if any)';
      logger.info(logText);
    }

    for (var i in panel) {
      var id = "#" + panel[i];
      var selector = $(id);
      if ( selector.length ) {
        // Set the return URL (user by news pager) for the News Banner
        // Cookies.set(cookieUserState, '{{page_url}}#news_panel', { expires: 365 });
        // user_state.previous_page = "{{page_url}}"
        // j1.setUserStateCookie( user_state );
        var panel_data_path = '{{panel_data_path}} ' + id + ' > *';
        selector.load( panel_data_path, cb_load_closure( id ) );
      }
    }

    return true;
  }, // END initPanel

  // -------------------------------------------------------------------------
  // AJAX loader for page footer used with J1 Template fo all pages
  // -------------------------------------------------------------------------
  // ToDo:
  initFooter: function ( options ) {
    var logger;

    logger  = log4javascript.getLogger('j1.Template.initFooter');
    logText = 'initFooter: Start initialization';
    logger.info(logText);

    var cb_load_closure = function(footer_id) {
      return function ( responseTxt, statusTxt, xhr ) {
        if ( statusTxt ==  'success' ) {
          logText = 'initFooter: Footer on ID ' +footer_id+ ' loaded successfully';
          logger.info(logText);
        }
        if ( statusTxt == 'error' ) {
          logText = 'initFooter: Footer on ID ' +footer_id+ ' loading failed. Error: ' + xhr.status + ': ' + xhr.statusText;
          logger.error(logText);
          // Set|Log status
          state = 'failed';
          logger.error('state: ' + state);
        }
      };
    };

    var id = '#' + '{{footer_id}}';
    var selector = $(id);
    if ( selector.length ) {
      var footer_data_path = '{{footer_data_path}}';
      selector.load( footer_data_path, cb_load_closure( id ) );
    }

    return true;
  }, // END initFooter

  // -------------------------------------------------------------------------
  // Create copy-to-clipboard for all pages
  // -------------------------------------------------------------------------
  // ToDo:
  initClipboard: function ( options ) {
    var logger;

    logger  = log4javascript.getLogger('j1.Template.initClipboard');
    logText = 'initClipboard: Start initialization';
    logger.info(logText);

    // insert copy to clipboard button before all elements having a
    // class of ".highlight" assigned to (e.g. Asciidoc source blocks)
    $('.highlight').each(function () {
      // Check if no clipboard should be applied
      var isNoClip = $(this).closest('.noclip');
      if ( isNoClip.length == 0) {
        var btnHtml = '<div class="j1-clipboard"><span class="btn-clipboard j1-tooltip" title="Copy to clipboard">Copy</span></div>';
        var bumms = $(this).closest('.noclip');
        $(this).before(btnHtml);
        $('.btn-clipboard').tooltip();
      }
    });
    var clipboard = new Clipboard( '.btn-clipboard', {
      target: function target(trigger) {
        return trigger.parentNode.nextElementSibling;
      }
    });

    // Manage clipboard events
    clipboard.on('success', function (e) {
      $(e.trigger).attr('title', 'Copied!').tooltip('_fixTitle').tooltip('show').attr('title', 'Copy to clipboard').tooltip('_fixTitle');
      var logger = log4javascript.getLogger('j1.Template.adapterrr');
      var logText = 'initClipboard: Copy-To-Clipboard sucessfull';
      logger.info(logText);
      /* Cleanup clipped data for trailing numbers */
      var splitted = e.text.split('\n');
      var concat;
      var i;
      for (i=0; i<splitted.length; i++) {
        concat += splitted[i].replace(/^\s+\d+/, '');
      }
      e.clearSelection();
    });
    clipboard.on('error', function (e) {
      var fallbackMsg = /Mac/i.test(navigator.userAgent) ? 'Press \u2318 to copy' : 'Press Ctrl-C to copy';
      logger = log4javascript.getLogger('j1.Template.adapterrr');
      logText = 'initClipboard: Copy-To-Clipboard failed. Use fallback.';
      logger.warn(logText);
      $(e.trigger).attr('title', fallbackMsg).tooltip('_fixTitle').tooltip('show').attr('title', 'Copy to clipboard').tooltip('_fixTitle');
    });

    return true;
  }, // END initClipboard

  // -------------------------------------------------------------------------
  // Show the page after timeout of {{hideOnReload}} ms
  // Display the page if the (text) header is signaled as 'initialized'
  // AND the menubar is signaled as (state) 'finished'
  // -------------------------------------------------------------------------
  displayPage: function ( options ) {
    var settings          = options;
    var appDetected       = j1.appDetected();
    var authClientEnabled = j1.authClientEnabled();
    var user_state        = {};
    var web_session_state = {};
    var flickerTimeout    = {{site.data.j1_config.flicker_timeout}}
    var current_page;
    var previous_page;
    var logger;

    logger  = log4javascript.getLogger('j1.Template.displayPage');
    logText = 'Finalize current page';
    logger.info(logText);

    var headerLoaded = setInterval(function() {
      if ( j1.MastHead.state() === 'initialized' && j1.Navigator.state() === 'finished' ) {
        // pause the display of current page for flicker timeout [ms]
        setTimeout( function() { 
          // display current page
          $('#no_flicker').css('display', 'block');
          user_state    = j1.getUserStateCookie();

          previous_page = j1.getPrevPage();
          current_page  = window.location.pathname;

          if ( previous_page !== current_page ) {
            logText = 'Page change detected';
            logger.info(logText);
            logText = 'Previous page: ' + previous_page;
            logger.info(logText);
            logText = 'Current page: ' + current_page;
            logger.info(logText);
          }

          // Show or Hide cookie icon
          if ( user_state.cookies_accepted === 'accepted' ) {

            // Display cookie icon
            $('#quickLinksCookieButton').css('display', 'block'); 
          } else {
            logText = 'Hide cookie icon';
            logger.info(logText);
            // Display cookie icon
            $('#quickLinksCookieButton').css('display', 'none'); 
          }

          // Show or Hide signin icon
          if ( appDetected && authClientEnabled ) {
            logText = 'Show signIn icon';
            logger.info(logText);
            $('#quickLinksSignInOutButton').css('display', 'block'); 
          } else {
            logText = 'Hide signIn icon';
            logger.info(logText);
            $('#quickLinksSignInOutButton').css('display', 'none'); 
          }

          $('#no_flicker').css('display', 'block'); // display current page
          j1.scrollTo(); // scroll to an anchor element this page (if any)

          // Update sidebar
          logger.info('Update sidebar');
          j1.updateSidebar();

          // Set|Log status
          state = 'finished';
          logText = 'state: ' + state;
          logger.info(logText);
          logText = "J1 Template successfully initialized";
          logger.info(logText);
        }, flickerTimeout );
        // clear interval checking
        clearInterval(headerLoaded);
      }
    }, 50); // END setInterval

    return true;
  }, // END displayPage

  // -------------------------------------------------------------------------
  // Initialize BS Material Design
  // -------------------------------------------------------------------------
  // ToDo:
  initMDB: function ( options ) {
    var logger;    

    logger  = log4javascript.getLogger('j1.Template.initMDB');
    logText = 'initMDB: Start initMDB';
    logger.info(logText);

    $('body').bootstrapMaterialDesign();
    //new WOW().init();

    var logText = 'initMDB Finished initMDB';
    logger.info(logText);

    return true;
  }, // END initMDB

  // -------------------------------------------------------------------------
  // Helper functions
  // -------------------------------------------------------------------------

  // -------------------------------------------------------------------------
  //  Merge two objects (properties) and returns the resulting object
  //  TODO:  Improve comment, give synopsis and example
  // See: https://stackoverflow.com/questions/43109229/merge-default-options-containing-object-with-json-object
  // -------------------------------------------------------------------------
  mergeData: function () {

    var a = [].slice.call(arguments), o = a.shift();
    for(var i=0,l=a.length; i<l; i++){
      for(var p in a[i]){
        o[p] = a[i][p];
      }
    }

    return o;
  },  // END mergeData

  // -------------------------------------------------------------------------
  //  returns the current (processing) state of the module
  // -------------------------------------------------------------------------
  state: function () {
    return state;
  }, // END state

  // -------------------------------------------------------------------------
  //  returns the last vistited page
  // -------------------------------------------------------------------------
  getPrevPage: function () {
    return previous_page;
  }, // END getPrevPage

  // -------------------------------------------------------------------------
  //  Returns the content of a given cookie (name) if exists otherwise
  //  null is returned
  //  See: https://stackoverflow.com/questions/5968196/check-cookie-if-cookie-exists/5968306
  // -------------------------------------------------------------------------
  getCookie: function (name) {

    var dc      = document.cookie;
    var prefix  = name + "=";
    var begin   = dc.indexOf("; " + prefix);
    var end     = dc.length; // default to end of the string

    // found, and not in first position
    if (begin !== -1) {
      // exclude the "; "
      begin += 2;
    } else {
      //see if cookie is in first position
      begin = dc.indexOf(prefix);
      // not found at all or found as a portion of another cookie name
      if (begin === -1 || begin !== 0 ) return null;
    }

    // if ";" is found somewhere after the prefix position then "end" is
    // that position, otherwise it defaults to the end of the string
    if (dc.indexOf(";", begin) !== -1) {
      end = dc.indexOf(";", begin);
    }

    return decodeURI(dc.substring(begin + prefix.length, end) ).replace(/"/g, '');
  }, // END getCookie

  // -------------------------------------------------------------------------
  //  Clears all given cookies by name (except cookies set to httpOnly). 
  //  For all cookies, the expire date is set in the past to make all 
  //  cookies affectected a 'session' cookie. All session cookies are 
  //  deleted by the browser automatically if the last session (browser 
  //  window) is closed.
  //  See: https://stackoverflow.com/questions/179355/clearing-all-cookies-with-javascript
  // -------------------------------------------------------------------------
  deleteCookie: function (options) {
    var name        = options;
    var all_cookies = document.cookie.split("; ");

    if ( name === 'all' ) {
      for (var c = 0; c < all_cookies.length; c++) {
        var d = window.location.hostname.split(".");
        while (d.length > 0) {
          var cookieBase = encodeURIComponent(all_cookies[c].split(";")[0].split("=")[0]) + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT; domain=' + d.join('.') + ' ;path=';
          var p = location.pathname.split('/');
          document.cookie = cookieBase + '/';
          while (p.length > 0) {
            document.cookie = cookieBase + p.join('/');
            p.pop();
          };
          d.shift();
        }
      }
    } else {
      document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
    }

    return true;
  }, // END getCookie

  // -------------------------------------------------------------------------
  //  Returns the current user state collected from /status endpoint
  //  if the web is detected as an app
  // -------------------------------------------------------------------------
  getUserState: function (params, cbData) {

    var parameters = params == '' ? 'all' : params;
    var url = '/status?fields=' + parameters;

    if ( j1.existsCookie('j1.web.session') ) {
      $.ajax({
        url:      url,
        success:  function(data) {
          if (typeof data == 'string') {
            JSON.parse(data)
          }
          if (typeof data == 'object') {
            data
          }
          cbData(data);
        }
      });
    } else {
      return { authenticated : false };
    }

  }, // END getUserState

  // -------------------------------------------------------------------------
  //  Returns the content of J1 user state cookie (j1.user.state) as
  //  an object if exists, otherwise null is returned
  //  See: https://stackoverflow.com/questions/5968196/check-cookie-if-cookie-exists/5968306
  // -------------------------------------------------------------------------
  getUserStateCookie: function () {

    var user_state;
    var user_state_json;

    if ( j1.existsCookie ( 'j1.user.state' ) ) {
      user_state_json = window.atob(Cookies.get('j1.user.state'));
      user_state      = JSON.parse(user_state_json);
      return user_state;
    } else {
      return null;
    }

  }, // END getUserStateCookie

  // -------------------------------------------------------------------------
  //  Set|Update the content of J1 user state cookie (j1.user.state) if
  //  the cookie exists, otherwise the is created AND updated.
  //  See: https://stackoverflow.com/questions/5968196/check-cookie-if-cookie-exists/5968306
  // -------------------------------------------------------------------------
  setUserStateCookie: function ( state ) {

    // update existing cookie
    if ( j1.existsCookie ( 'j1.user.state' ) ) {
      state.writer                = 'web';
      var state_from_cookie_json  = window.atob(Cookies.get('j1.user.state'));
      var state_from_cookie       = JSON.parse(state_from_cookie_json);
      var user_state              = j1.mergeData( state_from_cookie, state );

      var user_state_json         = JSON.stringify( user_state );
      var user_state_encoded      = window.btoa(user_state_json);
      Cookies.set( 'j1.user.state', user_state_encoded, { expires: 365 } );
    } else {
      // create|initialize cookie
      var user_state              = state;
      var user_state_json         = JSON.stringify(user_state);
      var user_state_encoded      = window.btoa(user_state_json);
      Cookies.set( 'j1.user.state', user_state_encoded, { expires: 365 } );
    }

    return true;
  }, // END setUserStateCookie

  // -------------------------------------------------------------------------
  //  Returns the content of J1 web session cookie (j1.web.session) as
  //  an object if exists, otherwise null is returned
  //  See: https://stackoverflow.com/questions/5968196/check-cookie-if-cookie-exists/5968306
  // -------------------------------------------------------------------------
  getWebSessionCookie: function () {

    var session_data;

    if ( j1.existsCookie ( 'j1.web.session' ) ) {
      var session_json = window.atob(Cookies.get('j1.web.session'));
      session_data = JSON.parse(session_json);
      return session_data;
    } else {
      return null;
    }

  }, // END getWebSessionCookie

  // -------------------------------------------------------------------------
  //  Set the content of J1 session cookie (j1.user.state). If successful,
  //  true ist returned otherwise false
  //  See: https://stackoverflow.com/questions/5968196/check-cookie-if-cookie-exists/5968306
  // -------------------------------------------------------------------------
  setWebSessionCookie: function ( session_data ) {

    if ( j1.existsCookie ( 'j1.web.session' ) ) {
      session_data.writer = 'web';
      var session_json = JSON.stringify(session_data);
      var session_encoded = window.btoa(session_json);
      Cookies.set('j1.web.session', session_encoded );
      return true;
    } else {
      return false;
    }

  }, // END setWebSessionCookie

  // -------------------------------------------------------------------------
  //  returns true if a given cookie exists
  // -------------------------------------------------------------------------
  existsCookie: function (name) {

    var myCookie = j1.getCookie( name );

    if (myCookie == null) {
      return false;
    }
    else {
      return true;
    }

  }, // END existsCookie

  // -------------------------------------------------------------------------
  //  returns the preferred language taken form window.navigator
  //  See:
  //  https://stackoverflow.com/questions/1043339/javascript-for-detecting-browser-language-preference
  // -------------------------------------------------------------------------
  getLanguage: function () {

    var language = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage);

    return language;
  }, // END getLanguage

  // -------------------------------------------------------------------------
  //  returns the template version taken from site config ( _config.yml)
  // -------------------------------------------------------------------------
  getTemplateVersion: function () {
    return '{{template_version}}';
  }, // END getTemplateVersion

  // -------------------------------------------------------------------------
  // Scrolls smooth to any anchor referenced by an page URL
  // -------------------------------------------------------------------------
  // values for delay|offset are taken from TOC module (Tocbot)
  scrollTo: function () {

    // Unclear why a correction is needed sometimes. Disable for now.
    //var offset_correction = 75;
    var offset_correction = 25;
    var offset            = {{toccer_config.defaults.smoothScrollOffset}};
    var anchor_id         = window.location.href.split("#")[1];

    if (anchor_id) {
      var selector = $('.' + anchor_id + ', #' + anchor_id +',[name='+anchor_id+']');

      // scroll only, if an anchor is given with URL
      if (selector.length) {
        var delay     = {{toccer_config.defaults.smoothScrollDuration}};
        var scroll_to = parseInt( selector.offset().top - offset - offset_correction );
        //var scroll_to = selector.offset().top;
        $('html,body').animate({scrollTop: scroll_to}, delay,
          function () {
            // scroll the page one pixel back and forth
            // to get the right position for the NAV Module (Tocbot)
            $(window).scrollTop($(window).scrollTop()+1);
            $(window).scrollTop($(window).scrollTop()-1);
        });
      } else {
        // TODO: to be checked if this else is needed
        // scroll the page one pixel back and forth
        // to get the right position for the NAV Module (Tocbot)
        $(window).scrollTop($(window).scrollTop()+1);
        $(window).scrollTop($(window).scrollTop()-1);
      } // selector.length
    } else {
      // scroll the page one pixel back and forth
      // to get the right position for the NAV Module (Tocbot)
      $(window).scrollTop($(window).scrollTop()+1);
      $(window).scrollTop($(window).scrollTop()-1);
    } // END if anchor_id

    return true;
  }, // END scrollTo

  // -------------------------------------------------------------------------
  // Replace MACROs
  //
  // See: 
  //  https://stackoverflow.com/questions/5376431/wildcards-in-jquery-selectors
  //  https://stackoverflow.com/questions/16400072/jquery-each-only-affects-last-element
  //  https://dzone.com/articles/why-does-javascript-loop-only-use-last-value
  //  https://stackoverflow.com/questions/179713/how-to-change-the-href-for-a-hyperlink-using-jquery
  // -------------------------------------------------------------------------
  resolveMacros: function ( data ) {

    var logger;
    var logText;
    var user_state   = data;
    var app_detected = j1.existsCookie ( 'j1.web.session' );

    // Set default settings
    user_state.user_info_url        = '{{site.data.j1_config.user_info_url}}'
    user_state.theme_author_url     = '{{site.data.j1_config.theme_author_url}}'
    user_state.theme_version        = j1.getTemplateVersion();
    // user_state.provider_permissions = user_state.provider_permissions;

    // Resolve all macros
    $('[id^=macro-]').each(function() {

      $('#macro-provider-name').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??provider-name', user_state.provider));
        // jadams, 2019-04-15: unclear why 'href' by base URL
        this.href = this.href.replace(/.*\/??provider-url/, user_state.provider_url);
      });
      $('#macro-user-name').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??user-name', user_state.user_name));
        this.href = this.href.replace(/.*\/??user-info-url/, user_state.user_info_url);
      });
      // Replace sidebar|State (currently disabled)
      // $('#macro-user-state').each(function() {
      //   var $this = $(this);
      //   var $html = $this.html();
      //   $this.html($html.replace('??user-state', user_state.status));
      //   this.href = this.href.replace(/.*\/??user-info-url/, user_state.user_info_url);
      // });
      $('#macro-provider-permissions').each(function() {
        var $this = $(this);
        var $html = $this.html();                    
        $this.html($html.replace('??provider-permissions', user_state.provider_permissions));
        //jadams, 2019-04-16: Workaround for already set permissins ("all")
        //$this.html($html.replace(/all|public|protected|private/, user_state.provider_permissions));
        this.href = this.href.replace(/.*\/??user-info-url/, user_state.user_info_url);
      });
      $('#macro-provider-membership').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??provider-membership', user_state.provider_membership));
        this.href = this.href.replace(/.*\/??user-info-url/, user_state.user_info_url);
      });
      $('#macro-cookie-state').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??cookie-state', user_state.cookies_accepted));
        this.href = this.href.replace(/.*\/??user-info-url/, user_state.user_info_url);
      });
      $('#macro-theme-name').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??theme-name', user_state.theme_name));
      });
      $('#macro-theme-author').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??theme-author', user_state.theme_author));
        this.href = this.href.replace(/.*\/??theme-author-url/, user_state.theme_author_url);
      });
      $('#macro-theme-version').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace('??theme-version', user_state.theme_version));
      });
    }); // END Replace macros

    return true;
  }, // END resolveMacros

  // -------------------------------------------------------------------------
  // Update MACROs
  //
  // Note:
  // To manage chaching situations with severals browsers (e.g. Chrome),
  // some of the "values" set ealier by j1.resolveMacros needs to be
  // updated
  // -------------------------------------------------------------------------
  updateMacros: function ( data ) {

    var user_state = data;
    var logger;
    var logText;

    // Replace Macros
    $('[id^=macro-]').each(function() {
      //  Update sidebar|Cookies
      $('#macro-cookie-state').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace(/accepted|declined|pending/, user_state.cookies_accepted));
      });
      // Update sidebar|State (currently disabled)
      // $('#macro-user-state').each(function() {
      //   var $this = $(this);
      //   var $html = $this.html();
      //   $this.html($html.replace(/active|blocked|pending/, user_state.status));
      // });
      $('#macro-provider-permissions').each(function() {
        var $this = $(this);
        var $html = $this.html();                    
        //$this.html($html.replace(/all|public|protected|private/g, user_state.provider_permissions));
        //$this.html($html.replace(/.*/, 'Permissions: ' + user_state.provider_permissions));
        $this.html($html.replace(/public.*|protected.*|private.*|blocked.*/, user_state.provider_permissions));
      });
      $('#macro-provider-membership').each(function() {
        var $this = $(this);
        var $html = $this.html();
        $this.html($html.replace(/guest|member/, user_state.provider_membership));
      });
    });

    return true;
  }, // END updateMacros

  // -------------------------------------------------------------------------
  // Detect if a page has changed
  //
  // Note:
  // Returns true on a change. Otherwise false
  // some of the "values" set ealier by j1.resolveMacros needs to be
  // updated
  // -------------------------------------------------------------------------
  hasPageChanged: function ( data ) {

    var user_state = data;
    var logger;
    var logText;

    if (user_state.previous_page == user_state.current_page ) {
      return false;
    } else {
      return true;
    }

  }, // END hasPageChanged

  // -------------------------------------------------------------------------
  // updateCookies:
  //
  // -------------------------------------------------------------------------
  updateCookies: function ( data ) {
    var user_state        = data;
    var appDetected       = j1.appDetected();
    var authClientEnabled = j1.authClientEnabled();
    var cookiesDeclined   = user_state.cookies_accepted === 'declined' ? true : false;
    var userAuthenticated;
    var web_session_state;
    var json_data;
    var logger;    

    logger = log4javascript.getLogger('j1.Template.updateCookies');

    // Update cookies
    if ( appDetected ) {
      web_session_state = j1.getWebSessionCookie();
      userAuthenticated = web_session_state.authenticated === 'true' ? true : false;

      if ( authClientEnabled ) {
        user_state.authenticated = userAuthenticated;

        if ( userAuthenticated ) { 
          user_state.provider               = web_session_state.provider;
          user_state.user_name              = web_session_state.user_name;
          user_state.provider_membership    = web_session_state.provider_membership;
          if ( cookiesDeclined ) {
            user_state.provider_permissions = 'blocked';
          } else {
            user_state.provider_permissions = web_session_state.provider_permissions;
          }
        } else {
          // Set default credentials if user is signed out
          user_state.provider               = '{{site.data.j1_config.user.provider}}';
          user_state.user_name              = '{{site.data.j1_config.user.user_name}}';
          user_state.status                 = 'active';
          user_state.provider_membership    = '{{site.data.j1_config.user.provider_membership}}';
          if ( cookiesDeclined ) {
            user_state.provider_permissions = 'blocked';
          } else {
            user_state.provider_permissions = '{{site.data.j1_config.user.provider_permissions}}';
          }                        
        }
      }

      web_session_state.writer           = 'web';
      web_session_state.requested_page   = window.location.pathname;
      web_session_state.cookies_accepted = user_state.cookies_accepted; 
      j1.setWebSessionCookie( web_session_state );

      json_data = JSON.stringify( web_session_state, null, 2 ); // JSON pretty print
      logText = 'Web Session data: ' + json_data;
      logger.info(logText);
    }

    j1.setUserStateCookie( user_state );
    json_data = JSON.stringify( user_state, undefined, 2); // JSON pretty print
    logText = 'User State data: ' + json_data;
    logger.info(logText);

    return true;     
  }, // END updateCookies

  // -------------------------------------------------------------------------
  // updateSidebar
  //
  // Note:
  // -------------------------------------------------------------------------
  updateSidebar: function () {
    var user_state = j1.getUserStateCookie();
    var json_data;
    var logger;     

    logger = log4javascript.getLogger('j1.Template.updateSidebar');

    if (!user_state) {
      user_state                      = {};
      user_state.status               = 'pending';
      user_state.user_name            = '{{site.data.j1_config.user.user_name}}';
      user_state.provider             = '{{site.data.j1_config.user.provider}}';
      user_state.provider_url         = '{{site.data.j1_config.user.provider_url}}';        
      user_state.provider_membership  = '{{site.data.j1_config.user.provider_membership}}';
      user_state.provider_permissions = 'blocked';

    }

    json_data = JSON.stringify( user_state, undefined, 2); // JSON pretty print
    logText = 'User State data: ' + json_data;
    logger.info(logText);

    // Replace Macro placeholders to values
    j1.resolveMacros( user_state );
    // Replace Macro values only
    j1.updateMacros( user_state );

    return true;
  }, // END updateSidebar

  // -------------------------------------------------------------------------
  //  authClientEnabled:
  //  Returns the state of the authClient
  // -------------------------------------------------------------------------
  authClientEnabled: function () {

    var authClientEnabled     = {{site.j1_auth.enabled}};
    var hideOnCookiesDeclined = {{site.data.modules.j1_navigator.nav_authclient.hide_on_cookies_declined}};
    var user_state            = j1.getUserStateCookie();

    if ( authClientEnabled )  {
      if (hideOnCookiesDeclined) {
        if ( user_state.cookies_accepted === 'declined' || user_state.cookies_accepted == 'pending') {
            return false;
          } else {
            return true;
          }
      } else {
        return true;
      }
    } else {
      return false;
    }      
  }, // END authClientEnabled

  // -------------------------------------------------------------------------
  //  appDetected:
  //  Returns true if a web session cookie exists
  // -------------------------------------------------------------------------
  appDetected: function () {
    var webSessionCookie = 'j1.web.session';

    return j1.existsCookie ( webSessionCookie );
  } // END appDetected

} // END j1 (return)

}(j1, window);

{% endcapture %}

{{ cache | strip_empty_lines }} {% assign cache = nil %}