text: /**
   * @file citi_bike.js
   * The plugin that retrieves the information from the citi bike module.
   *
   * Drupal Core: 8.x
   *
   */
  (function ($, Drupal, drupalSettings) {
  
    // Enable Strict Mode
    'use strict';
  
    // Add data Tables Plugin to the results
    // $(document).ready(function() {
    //   $('#citi-bike-stations-container').DataTable();
    // } );
  
  
    /**
     * Attach our blocks to the plugin within context
     * @type {{attach: Drupal.behaviors.citi_bike.attach}}
     */
    Drupal.behaviors.citi_bike = {
      attach: function (context, settings) {
  
        $('.citi-bike-container', context).once('citi-bike-init').each(function(){
          var delta = $(this).attr('id');
          $(this).facebookAlbum(settings.citi_bike[delta]);
        });
  
      }
    };
  
    // Enable Strict Mode
    'use strict';
  
    /**
     * Unchanging vars
     */
    var path_prefix = drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix;
  
    var isWebkit = false;
    if(navigator.userAgent.indexOf('AppleWebKit') != -1) {
      isWebkit = true;
    }
  
    $.fn.facebookAlbum = function(options) {
      var $this = $(this);
  
      var loading = false;
  
      /**
       * Elements
       */
      var $albumContainer = $('#citi-bike-stations-container', $this);
      var $albumLoader = $('.loading-icon', $this);
  
  
      /*=========================================
       =            HELPER FUNCTIONS            =
       =========================================*/
  
      // Display/Hide throbber
      var loadingIcon = function (show) {
        if (show) {
          var icon = '<div class="spinner">';
          icon += '<div class="rect1"></div>';
          icon += '<div class="rect2"></div>';
          icon += '<div class="rect3"></div>';
          icon += '<div class="rect4"></div>';
          icon += '<div class="rect5"></div>';
          icon += '</div>';
  
          $albumLoader.html(icon);
        }
        else {
          $albumLoader.html('');
        }
      };
  
  
      /*=========================================
      =            PRIVATE FUNCTIONS            =
      =========================================*/
  
      /**
       * Load albums and set up tiles
       * @param after
       */
      var loadAlbums = function(after) {
  
        $.ajax({
          url: 'http://drupal8.dev/citibike/stations',
          dataType: 'json',
          success: function(response) { console.log(response.data.content);
  
            if (response.data != null && response.data.content != null) {    
  
              $albumContainer.append(response.data.content);
              $albumContainer.fadeIn(500);
              
              loading = false;
              loadingIcon(false);
            }
          }
        });
      };
  
      /**
       * Initialize the albums
       */
      var initAlbum = function() {
        loading = false;
  
        $albumContainer.html('');
  
        loadingIcon(true);
        loadAlbums(null);
      };
  
      /**
       * Start it up!
       */
      initAlbum();
  
    };
  
  })(jQuery, Drupal, drupalSettings);
  ;
  /**
   * @file
   * Adds an HTML element and method to trigger audio UAs to read system messages.
   *
   * Use {@link Drupal.announce} to indicate to screen reader users that an
   * element on the page has changed state. For instance, if clicking a link
   * loads 10 more items into a list, one might announce the change like this.
   *
   * @example
   * $('#search-list')
   *   .on('itemInsert', function (event, data) {
   *     // Insert the new items.
   *     $(data.container.el).append(data.items.el);
   *     // Announce the change to the page contents.
   *     Drupal.announce(Drupal.t('@count items added to @container',
   *       {'@count': data.items.length, '@container': data.container.title}
   *     ));
   *   });
   */
  
  (function (Drupal, debounce) {
  
    'use strict';
  
    var liveElement;
    var announcements = [];
  
    /**
     * Builds a div element with the aria-live attribute and add it to the DOM.
     *
     * @type {Drupal~behavior}
     *
     * @prop {Drupal~behaviorAttach} attach
     *   Attaches the behavior for drupalAnnouce.
     */
    Drupal.behaviors.drupalAnnounce = {
      attach: function (context) {
        // Create only one aria-live element.
        if (!liveElement) {
          liveElement = document.createElement('div');
          liveElement.id = 'drupal-live-announce';
          liveElement.className = 'visually-hidden';
          liveElement.setAttribute('aria-live', 'polite');
          liveElement.setAttribute('aria-busy', 'false');
          document.body.appendChild(liveElement);
        }
      }
    };
  
    /**
     * Concatenates announcements to a single string; appends to the live region.
     */
    function announce() {
      var text = [];
      var priority = 'polite';
      var announcement;
  
      // Create an array of announcement strings to be joined and appended to the
      // aria live region.
      var il = announcements.length;
      for (var i = 0; i < il; i++) {
        announcement = announcements.pop();
        text.unshift(announcement.text);
        // If any of the announcements has a priority of assertive then the group
        // of joined announcements will have this priority.
        if (announcement.priority === 'assertive') {
          priority = 'assertive';
        }
      }
  
      if (text.length) {
        // Clear the liveElement so that repeated strings will be read.
        liveElement.innerHTML = '';
        // Set the busy state to true until the node changes are complete.
        liveElement.setAttribute('aria-busy', 'true');
        // Set the priority to assertive, or default to polite.
        liveElement.setAttribute('aria-live', priority);
        // Print the text to the live region. Text should be run through
        // Drupal.t() before being passed to Drupal.announce().
        liveElement.innerHTML = text.join('\n');
        // The live text area is updated. Allow the AT to announce the text.
        liveElement.setAttribute('aria-busy', 'false');
      }
    }
  
    /**
     * Triggers audio UAs to read the supplied text.
     *
     * The aria-live region will only read the text that currently populates its
     * text node. Replacing text quickly in rapid calls to announce results in
     * only the text from the most recent call to {@link Drupal.announce} being
     * read. By wrapping the call to announce in a debounce function, we allow for
     * time for multiple calls to {@link Drupal.announce} to queue up their
     * messages. These messages are then joined and append to the aria-live region
     * as one text node.
     *
     * @param {string} text
     *   A string to be read by the UA.
     * @param {string} [priority='polite']
     *   A string to indicate the priority of the message. Can be either
     *   'polite' or 'assertive'.
     *
     * @return {function}
     *   The return of the call to debounce.
     *
     * @see http://www.w3.org/WAI/PF/aria-practices/#liveprops
     */
    Drupal.announce = function (text, priority) {
      // Save the text and priority into a closure variable. Multiple simultaneous
      // announcements will be concatenated and read in sequence.
      announcements.push({
        text: text,
        priority: priority
      });
      // Immediately invoke the function that debounce returns. 200 ms is right at
      // the cusp where humans notice a pause, so we will wait
      // at most this much time before the set of queued announcements is read.
      return (debounce(announce, 200)());
    };
  }(Drupal, Drupal.debounce));
  ;
  window.matchMedia||(window.matchMedia=function(){"use strict";var e=window.styleMedia||window.media;if(!e){var t=document.createElement("style"),i=document.getElementsByTagName("script")[0],n=null;t.type="text/css";t.id="matchmediajs-test";i.parentNode.insertBefore(t,i);n="getComputedStyle"in window&&window.getComputedStyle(t,null)||t.currentStyle;e={matchMedium:function(e){var i="@media "+e+"{ #matchmediajs-test { width: 1px; } }";if(t.styleSheet){t.styleSheet.cssText=i}else{t.textContent=i}return n.width==="1px"}}}return function(t){return{matches:e.matchMedium(t||"all"),media:t||"all"}}}());
  ;
  (function(){if(window.matchMedia&&window.matchMedia("all").addListener){return false}var e=window.matchMedia,i=e("only all").matches,n=false,t=0,a=[],r=function(i){clearTimeout(t);t=setTimeout(function(){for(var i=0,n=a.length;i<n;i++){var t=a[i].mql,r=a[i].listeners||[],o=e(t.media).matches;if(o!==t.matches){t.matches=o;for(var s=0,l=r.length;s<l;s++){r[s].call(window,t)}}}},30)};window.matchMedia=function(t){var o=e(t),s=[],l=0;o.addListener=function(e){if(!i){return}if(!n){n=true;window.addEventListener("resize",r,true)}if(l===0){l=a.push({mql:o,listeners:s})}s.push(e)};o.removeListener=function(e){for(var i=0,n=s.length;i<n;i++){if(s[i]===e){s.splice(i,1)}}};return o}})();
  ;
  /**
   * @file
   * Builds a nested accordion widget.
   *
   * Invoke on an HTML list element with the jQuery plugin pattern.
   *
   * @example
   * $('.toolbar-menu').drupalToolbarMenu();
   */
  
  (function ($, Drupal, drupalSettings) {
  
    'use strict';
  
    /**
     * Store the open menu tray.
     */
    var activeItem = Drupal.url(drupalSettings.path.currentPath);
  
    $.fn.drupalToolbarMenu = function () {
  
      var ui = {
        handleOpen: Drupal.t('Extend'),
        handleClose: Drupal.t('Collapse')
      };
  
      /**
       * Handle clicks from the disclosure button on an item with sub-items.
       *
       * @param {Object} event
       *   A jQuery Event object.
       */
      function toggleClickHandler(event) {
        var $toggle = $(event.target);
        var $item = $toggle.closest('li');
        // Toggle the list item.
        toggleList($item);
        // Close open sibling menus.
        var $openItems = $item.siblings().filter('.open');
        toggleList($openItems, false);
      }
  
      /**
       * Handle clicks from a menu item link.
       *
       * @param {Object} event
       *   A jQuery Event object.
       */
      function linkClickHandler(event) {
        // If the toolbar is positioned fixed (and therefore hiding content
        // underneath), then users expect clicks in the administration menu tray
        // to take them to that destination but for the menu tray to be closed
        // after clicking: otherwise the toolbar itself is obstructing the view
        // of the destination they chose.
        if (!Drupal.toolbar.models.toolbarModel.get('isFixed')) {
          Drupal.toolbar.models.toolbarModel.set('activeTab', null);
        }
        // Stopping propagation to make sure that once a toolbar-box is clicked
        // (the whitespace part), the page is not redirected anymore.
        event.stopPropagation();
      }
  
      /**
       * Toggle the open/close state of a list is a menu.
       *
       * @param {jQuery} $item
       *   The li item to be toggled.
       *
       * @param {Boolean} switcher
       *   A flag that forces toggleClass to add or a remove a class, rather than
       *   simply toggling its presence.
       */
      function toggleList($item, switcher) {
        var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
        switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
        // Toggle the item open state.
        $item.toggleClass('open', switcher);
        // Twist the toggle.
        $toggle.toggleClass('open', switcher);
        // Adjust the toggle text.
        $toggle
          .find('.action')
          // Expand Structure, Collapse Structure.
          .text((switcher) ? ui.handleClose : ui.handleOpen);
      }
  
      /**
       * Add markup to the menu elements.
       *
       * Items with sub-elements have a list toggle attached to them. Menu item
       * links and the corresponding list toggle are wrapped with in a div
       * classed with .toolbar-box. The .toolbar-box div provides a positioning
       * context for the item list toggle.
       *
       * @param {jQuery} $menu
       *   The root of the menu to be initialized.
       */
      function initItems($menu) {
        var options = {
          class: 'toolbar-icon toolbar-handle',
          action: ui.handleOpen,
          text: ''
        };
        // Initialize items and their links.
        $menu.find('li > a').wrap('<div class="toolbar-box">');
        // Add a handle to each list item if it has a menu.
        $menu.find('li').each(function (index, element) {
          var $item = $(element);
          if ($item.children('ul.toolbar-menu').length) {
            var $box = $item.children('.toolbar-box');
            options.text = Drupal.t('@label', {'@label': $box.find('a').text()});
            $item.children('.toolbar-box')
              .append(Drupal.theme('toolbarMenuItemToggle', options));
          }
        });
      }
  
      /**
       * Adds a level class to each list based on its depth in the menu.
       *
       * This function is called recursively on each sub level of lists elements
       * until the depth of the menu is exhausted.
       *
       * @param {jQuery} $lists
       *   A jQuery object of ul elements.
       *
       * @param {number} level
       *   The current level number to be assigned to the list elements.
       */
      function markListLevels($lists, level) {
        level = (!level) ? 1 : level;
        var $lis = $lists.children('li').addClass('level-' + level);
        $lists = $lis.children('ul');
        if ($lists.length) {
          markListLevels($lists, level + 1);
        }
      }
  
      /**
       * On page load, open the active menu item.
       *
       * Marks the trail of the active link in the menu back to the root of the
       * menu with .menu-item--active-trail.
       *
       * @param {jQuery} $menu
       *   The root of the menu.
       */
      function openActiveItem($menu) {
        var pathItem = $menu.find('a[href="' + location.pathname + '"]');
        if (pathItem.length && !activeItem) {
          activeItem = location.pathname;
        }
        if (activeItem) {
          var $activeItem = $menu.find('a[href="' + activeItem + '"]').addClass('menu-item--active');
          var $activeTrail = $activeItem.parentsUntil('.root', 'li').addClass('menu-item--active-trail');
          toggleList($activeTrail, true);
        }
      }
  
      // Return the jQuery object.
      return this.each(function (selector) {
        var $menu = $(this).once('toolbar-menu');
        if ($menu.length) {
          // Bind event handlers.
          $menu
            .on('click.toolbar', '.toolbar-box', toggleClickHandler)
            .on('click.toolbar', '.toolbar-box a', linkClickHandler);
  
          $menu.addClass('root');
          initItems($menu);
          markListLevels($menu);
          // Restore previous and active states.
          openActiveItem($menu);
        }
      });
    };
  
    /**
     * A toggle is an interactive element often bound to a click handler.
     *
     * @param {object} options
     *   Options for the button.
     * @param {string} options.class
     *   Class to set on the button.
     * @param {string} options.action
     *   Action for the button.
     * @param {string} options.text
     *   Used as label for the button.
     *
     * @return {string}
     *   A string representing a DOM fragment.
     */
    Drupal.theme.toolbarMenuItemToggle = function (options) {
      return '<button class="' + options['class'] + '"><span class="action">' + options.action + '</span><span class="label">' + options.text + '</span></button>';
    };
  
  }(jQuery, Drupal, drupalSettings));
  ;
  /**
   * @file
   * Defines the behavior of the Drupal administration toolbar.
   */
  
  (function ($, Drupal, drupalSettings) {
  
    'use strict';
  
    // Merge run-time settings with the defaults.
    var options = $.extend(
      {
        breakpoints: {
          'toolbar.narrow': '',
          'toolbar.standard': '',
          'toolbar.wide': ''
        }
      },
      drupalSettings.toolbar,
      // Merge strings on top of drupalSettings so that they are not mutable.
      {
        strings: {
          horizontal: Drupal.t('Horizontal orientation'),
          vertical: Drupal.t('Vertical orientation')
        }
      }
    );
  
    /**
     * Registers tabs with the toolbar.
     *
     * The Drupal toolbar allows modules to register top-level tabs. These may
     * point directly to a resource or toggle the visibility of a tray.
     *
     * Modules register tabs with hook_toolbar().
     *
     * @type {Drupal~behavior}
     *
     * @prop {Drupal~behaviorAttach} attach
     *   Attaches the toolbar rendering functionality to the toolbar element.
     */
    Drupal.behaviors.toolbar = {
      attach: function (context) {
        // Verify that the user agent understands media queries. Complex admin
        // toolbar layouts require media query support.
        if (!window.matchMedia('only screen').matches) {
          return;
        }
        // Process the administrative toolbar.
        $(context).find('#toolbar-administration').once('toolbar').each(function () {
  
          // Establish the toolbar models and views.
          var model = Drupal.toolbar.models.toolbarModel = new Drupal.toolbar.ToolbarModel({
            locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')) || false,
            activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')))
          });
          Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView({
            el: this,
            model: model,
            strings: options.strings
          });
          Drupal.toolbar.views.toolbarAuralView = new Drupal.toolbar.ToolbarAuralView({
            el: this,
            model: model,
            strings: options.strings
          });
          Drupal.toolbar.views.bodyVisualView = new Drupal.toolbar.BodyVisualView({
            el: this,
            model: model
          });
  
          // Render collapsible menus.
          var menuModel = Drupal.toolbar.models.menuModel = new Drupal.toolbar.MenuModel();
          Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView({
            el: $(this).find('.toolbar-menu-administration').get(0),
            model: menuModel,
            strings: options.strings
          });
  
          // Handle the resolution of Drupal.toolbar.setSubtrees.
          // This is handled with a deferred so that the function may be invoked
          // asynchronously.
          Drupal.toolbar.setSubtrees.done(function (subtrees) {
            menuModel.set('subtrees', subtrees);
            var theme = drupalSettings.ajaxPageState.theme;
            localStorage.setItem('Drupal.toolbar.subtrees.' + theme, JSON.stringify(subtrees));
            // Indicate on the toolbarModel that subtrees are now loaded.
            model.set('areSubtreesLoaded', true);
          });
  
          // Attach a listener to the configured media query breakpoints.
          for (var label in options.breakpoints) {
            if (options.breakpoints.hasOwnProperty(label)) {
              var mq = options.breakpoints[label];
              var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
              // Curry the model and the label of the media query breakpoint to
              // the mediaQueryChangeHandler function.
              mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
              // Fire the mediaQueryChangeHandler for each configured breakpoint
              // so that they process once.
              Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
            }
          }
  
          // Trigger an initial attempt to load menu subitems. This first attempt
          // is made after the media query handlers have had an opportunity to
          // process. The toolbar starts in the vertical orientation by default,
          // unless the viewport is wide enough to accommodate a horizontal
          // orientation. Thus we give the Toolbar a chance to determine if it
          // should be set to horizontal orientation before attempting to load
          // menu subtrees.
          Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
  
          $(document)
            // Update the model when the viewport offset changes.
            .on('drupalViewportOffsetChange.toolbar', function (event, offsets) {
              model.set('offsets', offsets);
            });
  
          // Broadcast model changes to other modules.
          model
            .on('change:orientation', function (model, orientation) {
              $(document).trigger('drupalToolbarOrientationChange', orientation);
            })
            .on('change:activeTab', function (model, tab) {
              $(document).trigger('drupalToolbarTabChange', tab);
            })
            .on('change:activeTray', function (model, tray) {
              $(document).trigger('drupalToolbarTrayChange', tray);
            });
  
          // If the toolbar's orientation is horizontal and no active tab is
          // defined then show the tray of the first toolbar tab by default (but
          // not the first 'Home' toolbar tab).
          if (Drupal.toolbar.models.toolbarModel.get('orientation') === 'horizontal' && Drupal.toolbar.models.toolbarModel.get('activeTab') === null) {
            Drupal.toolbar.models.toolbarModel.set({
              activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0)
            });
          }
        });
      }
    };
  
    /**
     * Toolbar methods of Backbone objects.
     *
     * @namespace
     */
    Drupal.toolbar = {
  
      /**
       * A hash of View instances.
       *
       * @type {object.<string, Backbone.View>}
       */
      views: {},
  
      /**
       * A hash of Model instances.
       *
       * @type {object.<string, Backbone.Model>}
       */
      models: {},
  
      /**
       * A hash of MediaQueryList objects tracked by the toolbar.
       *
       * @type {object.<string, object>}
       */
      mql: {},
  
      /**
       * Accepts a list of subtree menu elements.
       *
       * A deferred object that is resolved by an inlined JavaScript callback.
       *
       * @type {jQuery.Deferred}
       *
       * @see toolbar_subtrees_jsonp().
       */
      setSubtrees: new $.Deferred(),
  
      /**
       * Respond to configured narrow media query changes.
       *
       * @param {Drupal.toolbar.ToolbarModel} model
       *   A toolbar model
       * @param {string} label
       *   Media query label.
       * @param {object} mql
       *   A MediaQueryList object.
       */
      mediaQueryChangeHandler: function (model, label, mql) {
        switch (label) {
          case 'toolbar.narrow':
            model.set({
              isOriented: mql.matches,
              isTrayToggleVisible: false
            });
            // If the toolbar doesn't have an explicit orientation yet, or if the
            // narrow media query doesn't match then set the orientation to
            // vertical.
            if (!mql.matches || !model.get('orientation')) {
              model.set({orientation: 'vertical'}, {validate: true});
            }
            break;
  
          case 'toolbar.standard':
            model.set({
              isFixed: mql.matches
            });
            break;
  
          case 'toolbar.wide':
            model.set({
              orientation: ((mql.matches) ? 'horizontal' : 'vertical')
            }, {validate: true});
            // The tray orientation toggle visibility does not need to be
            // validated.
            model.set({
              isTrayToggleVisible: mql.matches
            });
            break;
  
          default:
            break;
        }
      }
    };
  
    /**
     * A toggle is an interactive element often bound to a click handler.
     *
     * @return {string}
     *   A string representing a DOM fragment.
     */
    Drupal.theme.toolbarOrientationToggle = function () {
      return '<div class="toolbar-toggle-orientation"><div class="toolbar-lining">' +
        '<button class="toolbar-icon" type="button"></button>' +
        '</div></div>';
    };
  
    /**
     * Ajax command to set the toolbar subtrees.
     *
     * @param {Drupal.Ajax} ajax
     *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
     * @param {object} response
     *   JSON response from the Ajax request.
     * @param {number} [status]
     *   XMLHttpRequest status.
     */
    Drupal.AjaxCommands.prototype.setToolbarSubtrees = function (ajax, response, status) {
      Drupal.toolbar.setSubtrees.resolve(response.subtrees);
    };
  
  }(jQuery, Drupal, drupalSettings));
  ;
  /**
   * @file
   * A Backbone Model for collapsible menus.
   */
  
  (function (Backbone, Drupal) {
  
    'use strict';
  
    /**
     * Backbone Model for collapsible menus.
     *
     * @constructor
     *
     * @augments Backbone.Model
     */
    Drupal.toolbar.MenuModel = Backbone.Model.extend(/** @lends Drupal.toolbar.MenuModel# */{
  
      /**
       * @type {object}
       *
       * @prop {object} subtrees
       */
      defaults: /** @lends Drupal.toolbar.MenuModel# */{
  
        /**
         * @type {object}
         */
        subtrees: {}
      }
    });
  
  }(Backbone, Drupal));
  ;
  /**
   * @file
   * A Backbone Model for the toolbar.
   */
  
  (function (Backbone, Drupal) {
  
    'use strict';
  
    /**
     * Backbone model for the toolbar.
     *
     * @constructor
     *
     * @augments Backbone.Model
     */
    Drupal.toolbar.ToolbarModel = Backbone.Model.extend(/** @lends Drupal.toolbar.ToolbarModel# */{
  
      /**
       * @type {object}
       *
       * @prop activeTab
       * @prop activeTray
       * @prop isOriented
       * @prop isFixed
       * @prop areSubtreesLoaded
       * @prop isViewportOverflowConstrained
       * @prop orientation
       * @prop locked
       * @prop isTrayToggleVisible
       * @prop height
       * @prop offsets
       */
      defaults: /** @lends Drupal.toolbar.ToolbarModel# */{
  
        /**
         * The active toolbar tab. All other tabs should be inactive under
         * normal circumstances. It will remain active across page loads. The
         * active item is stored as an ID selector e.g. '#toolbar-item--1'.
         *
         * @type {string}
         */
        activeTab: null,
  
        /**
         * Represents whether a tray is open or not. Stored as an ID selector e.g.
         * '#toolbar-item--1-tray'.
         *
         * @type {string}
         */
        activeTray: null,
  
        /**
         * Indicates whether the toolbar is displayed in an oriented fashion,
         * either horizontal or vertical.
         *
         * @type {bool}
         */
        isOriented: false,
  
        /**
         * Indicates whether the toolbar is positioned absolute (false) or fixed
         * (true).
         *
         * @type {bool}
         */
        isFixed: false,
  
        /**
         * Menu subtrees are loaded through an AJAX request only when the Toolbar
         * is set to a vertical orientation.
         *
         * @type {bool}
         */
        areSubtreesLoaded: false,
  
        /**
         * If the viewport overflow becomes constrained, isFixed must be true so
         * that elements in the trays aren't lost off-screen and impossible to
         * get to.
         *
         * @type {bool}
         */
        isViewportOverflowConstrained: false,
  
        /**
         * The orientation of the active tray.
         *
         * @type {string}
         */
        orientation: 'vertical',
  
        /**
         * A tray is locked if a user toggled it to vertical. Otherwise a tray
         * will switch between vertical and horizontal orientation based on the
         * configured breakpoints. The locked state will be maintained across page
         * loads.
         *
         * @type {bool}
         */
        locked: false,
  
        /**
         * Indicates whether the tray orientation toggle is visible.
         *
         * @type {bool}
         */
        isTrayToggleVisible: false,
  
        /**
         * The height of the toolbar.
         *
         * @type {number}
         */
        height: null,
  
        /**
         * The current viewport offsets determined by {@link Drupal.displace}. The
         * offsets suggest how a module might position is components relative to
         * the viewport.
         *
         * @type {object}
         *
         * @prop {number} top
         * @prop {number} right
         * @prop {number} bottom
         * @prop {number} left
         */
        offsets: {
          top: 0,
          right: 0,
          bottom: 0,
          left: 0
        }
      },
  
      /**
       * @inheritdoc
       *
       * @param {object} attributes
       *   Attributes for the toolbar.
       * @param {object} options
       *   Options for the toolbar.
       *
       * @return {string|undefined}
       *   Returns an error message if validation failed.
       */
      validate: function (attributes, options) {
        // Prevent the orientation being set to horizontal if it is locked, unless
        // override has not been passed as an option.
        if (attributes.orientation === 'horizontal' && this.get('locked') && !options.override) {
          return Drupal.t('The toolbar cannot be set to a horizontal orientation when it is locked.');
        }
      }
    });
  
  }(Backbone, Drupal));
  ;
  /**
   * @file
   * A Backbone view for the body element.
   */
  
  (function ($, Drupal, Backbone) {
  
    'use strict';
  
    Drupal.toolbar.BodyVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.BodyVisualView# */{
  
      /**
       * Adjusts the body element with the toolbar position and dimension changes.
       *
       * @constructs
       *
       * @augments Backbone.View
       */
      initialize: function () {
        this.listenTo(this.model, 'change:orientation change:offsets change:activeTray change:isOriented change:isFixed change:isViewportOverflowConstrained', this.render);
      },
  
      /**
       * @inheritdoc
       */
      render: function () {
        var $body = $('body');
        var orientation = this.model.get('orientation');
        var isOriented = this.model.get('isOriented');
        var isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
  
        $body
          // We are using JavaScript to control media-query handling for two
          // reasons: (1) Using JavaScript let's us leverage the breakpoint
          // configurations and (2) the CSS is really complex if we try to hide
          // some styling from browsers that don't understand CSS media queries.
          // If we drive the CSS from classes added through JavaScript,
          // then the CSS becomes simpler and more robust.
          .toggleClass('toolbar-vertical', (orientation === 'vertical'))
          .toggleClass('toolbar-horizontal', (isOriented && orientation === 'horizontal'))
          // When the toolbar is fixed, it will not scroll with page scrolling.
          .toggleClass('toolbar-fixed', (isViewportOverflowConstrained || this.model.get('isFixed')))
          // Toggle the toolbar-tray-open class on the body element. The class is
          // applied when a toolbar tray is active. Padding might be applied to
          // the body element to prevent the tray from overlapping content.
          .toggleClass('toolbar-tray-open', !!this.model.get('activeTray'))
          // Apply padding to the top of the body to offset the placement of the
          // toolbar bar element.
          .css('padding-top', this.model.get('offsets').top);
      }
    });
  
  }(jQuery, Drupal, Backbone));
  ;
  /**
   * @file
   * A Backbone view for the collapsible menus.
   */
  
  (function ($, Backbone, Drupal) {
  
    'use strict';
  
    Drupal.toolbar.MenuVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.MenuVisualView# */{
  
      /**
       * Backbone View for collapsible menus.
       *
       * @constructs
       *
       * @augments Backbone.View
       */
      initialize: function () {
        this.listenTo(this.model, 'change:subtrees', this.render);
      },
  
      /**
       * @inheritdoc
       */
      render: function () {
        var subtrees = this.model.get('subtrees');
        // Add subtrees.
        for (var id in subtrees) {
          if (subtrees.hasOwnProperty(id)) {
            this.$el
              .find('#toolbar-link-' + id)
              .once('toolbar-subtrees')
              .after(subtrees[id]);
          }
        }
        // Render the main menu as a nested, collapsible accordion.
        if ('drupalToolbarMenu' in $.fn) {
          this.$el
            .children('.toolbar-menu')
            .drupalToolbarMenu();
        }
      }
    });
  
  }(jQuery, Backbone, Drupal));
  ;
  /**
   * @file
   * A Backbone view for the aural feedback of the toolbar.
   */
  
  (function (Backbone, Drupal) {
  
    'use strict';
  
    Drupal.toolbar.ToolbarAuralView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarAuralView# */{
  
      /**
       * Backbone view for the aural feedback of the toolbar.
       *
       * @constructs
       *
       * @augments Backbone.View
       *
       * @param {object} options
       *   Options for the view.
       * @param {object} options.strings
       *   Various strings to use in the view.
       */
      initialize: function (options) {
        this.strings = options.strings;
  
        this.listenTo(this.model, 'change:orientation', this.onOrientationChange);
        this.listenTo(this.model, 'change:activeTray', this.onActiveTrayChange);
      },
  
      /**
       * Announces an orientation change.
       *
       * @param {Drupal.toolbar.ToolbarModel} model
       *   The toolbar model in question.
       * @param {string} orientation
       *   The new value of the orientation attribute in the model.
       */
      onOrientationChange: function (model, orientation) {
        Drupal.announce(Drupal.t('Tray orientation changed to @orientation.', {
          '@orientation': orientation
        }));
      },
  
      /**
       * Announces a changed active tray.
       *
       * @param {Drupal.toolbar.ToolbarModel} model
       *   The toolbar model in question.
       * @param {HTMLElement} tray
       *   The new value of the tray attribute in the model.
       */
      onActiveTrayChange: function (model, tray) {
        var relevantTray = (tray === null) ? model.previous('activeTray') : tray;
        var action = (tray === null) ? Drupal.t('closed') : Drupal.t('opened');
        var trayNameElement = relevantTray.querySelector('.toolbar-tray-name');
        var text;
        if (trayNameElement !== null) {
          text = Drupal.t('Tray "@tray" @action.', {
            '@tray': trayNameElement.textContent, '@action': action
          });
        }
        else {
          text = Drupal.t('Tray @action.', {'@action': action});
        }
        Drupal.announce(text);
      }
    });
  
  }(Backbone, Drupal));
  ;
  /**
   * @file
   * A Backbone view for the toolbar element. Listens to mouse & touch.
   */
  
  (function ($, Drupal, drupalSettings, Backbone) {
  
    'use strict';
  
    Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarVisualView# */{
  
      /**
       * Event map for the `ToolbarVisualView`.
       *
       * @return {object}
       *   A map of events.
       */
      events: function () {
        // Prevents delay and simulated mouse events.
        var touchEndToClick = function (event) {
          event.preventDefault();
          event.target.click();
        };
  
        return {
          'click .toolbar-bar .toolbar-tab .trigger': 'onTabClick',
          'click .toolbar-toggle-orientation button': 'onOrientationToggleClick',
          'touchend .toolbar-bar .toolbar-tab .trigger': touchEndToClick,
          'touchend .toolbar-toggle-orientation button': touchEndToClick
        };
      },
  
      /**
       * Backbone view for the toolbar element. Listens to mouse & touch.
       *
       * @constructs
       *
       * @augments Backbone.View
       *
       * @param {object} options
       *   Options for the view object.
       * @param {object} options.strings
       *   Various strings to use in the view.
       */
      initialize: function (options) {
        this.strings = options.strings;
  
        this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render);
        this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
        this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
  
        // Add the tray orientation toggles.
        this.$el
          .find('.toolbar-tray .toolbar-lining')
          .append(Drupal.theme('toolbarOrientationToggle'));
  
        // Trigger an activeTab change so that listening scripts can respond on
        // page load. This will call render.
        this.model.trigger('change:activeTab');
      },
  
      /**
       * @inheritdoc
       *
       * @return {Drupal.toolbar.ToolbarVisualView}
       *   The `ToolbarVisualView` instance.
       */
      render: function () {
        this.updateTabs();
        this.updateTrayOrientation();
        this.updateBarAttributes();
        // Load the subtrees if the orientation of the toolbar is changed to
        // vertical. This condition responds to the case that the toolbar switches
        // from horizontal to vertical orientation. The toolbar starts in a
        // vertical orientation by default and then switches to horizontal during
        // initialization if the media query conditions are met. Simply checking
        // that the orientation is vertical here would result in the subtrees
        // always being loaded, even when the toolbar initialization ultimately
        // results in a horizontal orientation.
        //
        // @see Drupal.behaviors.toolbar.attach() where admin menu subtrees
        // loading is invoked during initialization after media query conditions
        // have been processed.
        if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) {
          this.loadSubtrees();
        }
        // Trigger a recalculation of viewport displacing elements. Use setTimeout
        // to ensure this recalculation happens after changes to visual elements
        // have processed.
        window.setTimeout(function () {
          Drupal.displace(true);
        }, 0);
        return this;
      },
  
      /**
       * Responds to a toolbar tab click.
       *
       * @param {jQuery.Event} event
       *   The event triggered.
       */
      onTabClick: function (event) {
        // If this tab has a tray associated with it, it is considered an
        // activatable tab.
        if (event.target.hasAttribute('data-toolbar-tray')) {
          var activeTab = this.model.get('activeTab');
          var clickedTab = event.target;
  
          // Set the event target as the active item if it is not already.
          this.model.set('activeTab', (!activeTab || clickedTab !== activeTab) ? clickedTab : null);
  
          event.preventDefault();
          event.stopPropagation();
        }
      },
  
      /**
       * Toggles the orientation of a toolbar tray.
       *
       * @param {jQuery.Event} event
       *   The event triggered.
       */
      onOrientationToggleClick: function (event) {
        var orientation = this.model.get('orientation');
        // Determine the toggle-to orientation.
        var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
        var locked = antiOrientation === 'vertical';
        // Remember the locked state.
        if (locked) {
          localStorage.setItem('Drupal.toolbar.trayVerticalLocked', 'true');
        }
        else {
          localStorage.removeItem('Drupal.toolbar.trayVerticalLocked');
        }
        // Update the model.
        this.model.set({
          locked: locked,
          orientation: antiOrientation
        }, {
          validate: true,
          override: true
        });
  
        event.preventDefault();
        event.stopPropagation();
      },
  
      /**
       * Updates the display of the tabs: toggles a tab and the associated tray.
       */
      updateTabs: function () {
        var $tab = $(this.model.get('activeTab'));
        // Deactivate the previous tab.
        $(this.model.previous('activeTab'))
          .removeClass('is-active')
          .prop('aria-pressed', false);
        // Deactivate the previous tray.
        $(this.model.previous('activeTray'))
          .removeClass('is-active');
  
        // Activate the selected tab.
        if ($tab.length > 0) {
          $tab
            .addClass('is-active')
            // Mark the tab as pressed.
            .prop('aria-pressed', true);
          var name = $tab.attr('data-toolbar-tray');
          // Store the active tab name or remove the setting.
          var id = $tab.get(0).id;
          if (id) {
            localStorage.setItem('Drupal.toolbar.activeTabID', JSON.stringify(id));
          }
          // Activate the associated tray.
          var $tray = this.$el.find('[data-toolbar-tray="' + name + '"].toolbar-tray');
          if ($tray.length) {
            $tray.addClass('is-active');
            this.model.set('activeTray', $tray.get(0));
          }
          else {
            // There is no active tray.
            this.model.set('activeTray', null);
          }
        }
        else {
          // There is no active tray.
          this.model.set('activeTray', null);
          localStorage.removeItem('Drupal.toolbar.activeTabID');
        }
      },
  
      /**
       * Update the attributes of the toolbar bar element.
       */
      updateBarAttributes: function () {
        var isOriented = this.model.get('isOriented');
        if (isOriented) {
          this.$el.find('.toolbar-bar').attr('data-offset-top', '');
        }
        else {
          this.$el.find('.toolbar-bar').removeAttr('data-offset-top');
        }
        // Toggle between a basic vertical view and a more sophisticated
        // horizontal and vertical display of the toolbar bar and trays.
        this.$el.toggleClass('toolbar-oriented', isOriented);
      },
  
      /**
       * Updates the orientation of the active tray if necessary.
       */
      updateTrayOrientation: function () {
        var orientation = this.model.get('orientation');
        // The antiOrientation is used to render the view of action buttons like
        // the tray orientation toggle.
        var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
        // Update the orientation of the trays.
        var $trays = this.$el.find('.toolbar-tray')
          .removeClass('toolbar-tray-horizontal toolbar-tray-vertical')
          .addClass('toolbar-tray-' + orientation);
  
        // Update the tray orientation toggle button.
        var iconClass = 'toolbar-icon-toggle-' + orientation;
        var iconAntiClass = 'toolbar-icon-toggle-' + antiOrientation;
        var $orientationToggle = this.$el.find('.toolbar-toggle-orientation')
          .toggle(this.model.get('isTrayToggleVisible'));
        $orientationToggle.find('button')
          .val(antiOrientation)
          .attr('title', this.strings[antiOrientation])
          .text(this.strings[antiOrientation])
          .removeClass(iconClass)
          .addClass(iconAntiClass);
  
        // Update data offset attributes for the trays.
        var dir = document.documentElement.dir;
        var edge = (dir === 'rtl') ? 'right' : 'left';
        // Remove data-offset attributes from the trays so they can be refreshed.
        $trays.removeAttr('data-offset-left data-offset-right data-offset-top');
        // If an active vertical tray exists, mark it as an offset element.
        $trays.filter('.toolbar-tray-vertical.is-active').attr('data-offset-' + edge, '');
        // If an active horizontal tray exists, mark it as an offset element.
        $trays.filter('.toolbar-tray-horizontal.is-active').attr('data-offset-top', '');
      },
  
      /**
       * Sets the tops of the trays so that they align with the bottom of the bar.
       */
      adjustPlacement: function () {
        var $trays = this.$el.find('.toolbar-tray');
        if (!this.model.get('isOriented')) {
          $trays.css('margin-top', 0);
          $trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical');
        }
        else {
          // The toolbar container is invisible. Its placement is used to
          // determine the container for the trays.
          $trays.css('margin-top', this.$el.find('.toolbar-bar').outerHeight());
        }
      },
  
      /**
       * Calls the endpoint URI that builds an AJAX command with the rendered
       * subtrees.
       *
       * The rendered admin menu subtrees HTML is cached on the client in
       * localStorage until the cache of the admin menu subtrees on the server-
       * side is invalidated. The subtreesHash is stored in localStorage as well
       * and compared to the subtreesHash in drupalSettings to determine when the
       * admin menu subtrees cache has been invalidated.
       */
      loadSubtrees: function () {
        var $activeTab = $(this.model.get('activeTab'));
        var orientation = this.model.get('orientation');
        // Only load and render the admin menu subtrees if:
        //   (1) They have not been loaded yet.
        //   (2) The active tab is the administration menu tab, indicated by the
        //       presence of the data-drupal-subtrees attribute.
        //   (3) The orientation of the tray is vertical.
        if (!this.model.get('areSubtreesLoaded') && typeof $activeTab.data('drupal-subtrees') !== 'undefined' && orientation === 'vertical') {
          var subtreesHash = drupalSettings.toolbar.subtreesHash;
          var theme = drupalSettings.ajaxPageState.theme;
          var endpoint = Drupal.url('toolbar/subtrees/' + subtreesHash);
          var cachedSubtreesHash = localStorage.getItem('Drupal.toolbar.subtreesHash.' + theme);
          var cachedSubtrees = JSON.parse(localStorage.getItem('Drupal.toolbar.subtrees.' + theme));
          var isVertical = this.model.get('orientation') === 'vertical';
          // If we have the subtrees in localStorage and the subtree hash has not
          // changed, then use the cached data.
          if (isVertical && subtreesHash === cachedSubtreesHash && cachedSubtrees) {
            Drupal.toolbar.setSubtrees.resolve(cachedSubtrees);
          }
          // Only make the call to get the subtrees if the orientation of the
          // toolbar is vertical.
          else if (isVertical) {
            // Remove the cached menu information.
            localStorage.removeItem('Drupal.toolbar.subtreesHash.' + theme);
            localStorage.removeItem('Drupal.toolbar.subtrees.' + theme);
            // The AJAX response's command will trigger the resolve method of the
            // Drupal.toolbar.setSubtrees Promise.
            Drupal.ajax({url: endpoint}).execute();
            // Cache the hash for the subtrees locally.
            localStorage.setItem('Drupal.toolbar.subtreesHash.' + theme, subtreesHash);
          }
        }
      }
    });
  
  }(jQuery, Drupal, drupalSettings, Backbone));
  ;
  /* jQuery Foundation Joyride Plugin 2.1 | Copyright 2012, ZURB | www.opensource.org/licenses/mit-license.php */
  (function(e,t,n){"use strict";var r={version:"2.0.3",tipLocation:"bottom",nubPosition:"auto",scroll:!0,scrollSpeed:300,timer:0,autoStart:!1,startTimerOnClick:!0,startOffset:0,nextButton:!0,tipAnimation:"fade",pauseAfter:[],tipAnimationFadeSpeed:300,cookieMonster:!1,cookieName:"joyride",cookieDomain:!1,cookiePath:!1,localStorage:!1,localStorageKey:"joyride",tipContainer:"body",modal:!1,expose:!1,postExposeCallback:e.noop,preRideCallback:e.noop,postRideCallback:e.noop,preStepCallback:e.noop,postStepCallback:e.noop,template:{link:'<a href="#close" class="joyride-close-tip">X</a>',timer:'<div class="joyride-timer-indicator-wrap"><span class="joyride-timer-indicator"></span></div>',tip:'<div class="joyride-tip-guide"><span class="joyride-nub"></span></div>',wrapper:'<div class="joyride-content-wrapper" role="dialog"></div>',button:'<a href="#" class="joyride-next-tip"></a>',modal:'<div class="joyride-modal-bg"></div>',expose:'<div class="joyride-expose-wrapper"></div>',exposeCover:'<div class="joyride-expose-cover"></div>'}},i=i||!1,s={},o={init:function(n){return this.each(function(){e.isEmptyObject(s)?(s=e.extend(!0,r,n),s.document=t.document,s.$document=e(s.document),s.$window=e(t),s.$content_el=e(this),s.$body=e(s.tipContainer),s.body_offset=e(s.tipContainer).position(),s.$tip_content=e("> li",s.$content_el),s.paused=!1,s.attempts=0,s.tipLocationPatterns={top:["bottom"],bottom:[],left:["right","top","bottom"],right:["left","top","bottom"]},o.jquery_check(),e.isFunction(e.cookie)||(s.cookieMonster=!1),(!s.cookieMonster||!e.cookie(s.cookieName))&&(!s.localStorage||!o.support_localstorage()||!localStorage.getItem(s.localStorageKey))&&(s.$tip_content.each(function(t){o.create({$li:e(this),index:t})}),s.autoStart&&(!s.startTimerOnClick&&s.timer>0?(o.show("init"),o.startTimer()):o.show("init"))),s.$document.on("click.joyride",".joyride-next-tip, .joyride-modal-bg",function(e){e.preventDefault(),s.$li.next().length<1?o.end():s.timer>0?(clearTimeout(s.automate),o.hide(),o.show(),o.startTimer()):(o.hide(),o.show())}),s.$document.on("click.joyride",".joyride-close-tip",function(e){e.preventDefault(),o.end()}),s.$window.bind("resize.joyride",function(t){if(s.$li){if(s.exposed&&s.exposed.length>0){var n=e(s.exposed);n.each(function(){var t=e(this);o.un_expose(t),o.expose(t)})}o.is_phone()?o.pos_phone():o.pos_default()}})):o.restart()})},resume:function(){o.set_li(),o.show()},nextTip:function(){s.$li.next().length<1?o.end():s.timer>0?(clearTimeout(s.automate),o.hide(),o.show(),o.startTimer()):(o.hide(),o.show())},tip_template:function(t){var n,r,i;return t.tip_class=t.tip_class||"",n=e(s.template.tip).addClass(t.tip_class),r=e.trim(e(t.li).html())+o.button_text(t.button_text)+s.template.link+o.timer_instance(t.index),i=e(s.template.wrapper),t.li.attr("data-aria-labelledby")&&i.attr("aria-labelledby",t.li.attr("data-aria-labelledby")),t.li.attr("data-aria-describedby")&&i.attr("aria-describedby",t.li.attr("data-aria-describedby")),n.append(i),n.first().attr("data-index",t.index),e(".joyride-content-wrapper",n).append(r),n[0]},timer_instance:function(t){var n;return t===0&&s.startTimerOnClick&&s.timer>0||s.timer===0?n="":n=o.outerHTML(e(s.template.timer)[0]),n},button_text:function(t){return s.nextButton?(t=e.trim(t)||"Next",t=o.outerHTML(e(s.template.button).append(t)[0])):t="",t},create:function(t){var n=t.$li.attr("data-button")||t.$li.attr("data-text"),r=t.$li.attr("class"),i=e(o.tip_template({tip_class:r,index:t.index,button_text:n,li:t.$li}));e(s.tipContainer).append(i)},show:function(t){var r={},i,u=[],a=0,f,l=null;if(s.$li===n||e.inArray(s.$li.index(),s.pauseAfter)===-1){s.paused?s.paused=!1:o.set_li(t),s.attempts=0;if(s.$li.length&&s.$target.length>0){t&&(s.preRideCallback(s.$li.index(),s.$next_tip),s.modal&&o.show_modal()),s.preStepCallback(s.$li.index(),s.$next_tip),u=(s.$li.data("options")||":").split(";"),a=u.length;for(i=a-1;i>=0;i--)f=u[i].split(":"),f.length===2&&(r[e.trim(f[0])]=e.trim(f[1]));s.tipSettings=e.extend({},s,r),s.tipSettings.tipLocationPattern=s.tipLocationPatterns[s.tipSettings.tipLocation],s.modal&&s.expose&&o.expose(),!/body/i.test(s.$target.selector)&&s.scroll&&o.scroll_to(),o.is_phone()?o.pos_phone(!0):o.pos_default(!0),l=e(".joyride-timer-indicator",s.$next_tip),/pop/i.test(s.tipAnimation)?(l.outerWidth(0),s.timer>0?(s.$next_tip.show(),l.animate({width:e(".joyride-timer-indicator-wrap",s.$next_tip).outerWidth()},s.timer)):s.$next_tip.show()):/fade/i.test(s.tipAnimation)&&(l.outerWidth(0),s.timer>0?(s.$next_tip.fadeIn(s.tipAnimationFadeSpeed),s.$next_tip.show(),l.animate({width:e(".joyride-timer-indicator-wrap",s.$next_tip).outerWidth()},s.timer)):s.$next_tip.fadeIn(s.tipAnimationFadeSpeed)),s.$current_tip=s.$next_tip,e(".joyride-next-tip",s.$current_tip).focus(),o.tabbable(s.$current_tip)}else s.$li&&s.$target.length<1?o.show():o.end()}else s.paused=!0},is_phone:function(){return i?i.mq("only screen and (max-width: 767px)"):s.$window.width()<767?!0:!1},support_localstorage:function(){return i?i.localstorage:!!t.localStorage},hide:function(){s.modal&&s.expose&&o.un_expose(),s.modal||e(".joyride-modal-bg").hide(),s.$current_tip.hide(),s.postStepCallback(s.$li.index(),s.$current_tip)},set_li:function(e){e?(s.$li=s.$tip_content.eq(s.startOffset),o.set_next_tip(),s.$current_tip=s.$next_tip):(s.$li=s.$li.next(),o.set_next_tip()),o.set_target()},set_next_tip:function(){s.$next_tip=e(".joyride-tip-guide[data-index="+s.$li.index()+"]")},set_target:function(){var t=s.$li.attr("data-class"),n=s.$li.attr("data-id"),r=function(){return n?e(s.document.getElementById(n)):t?e("."+t).filter(":visible").first():e("body")};s.$target=r()},scroll_to:function(){var t,n;t=s.$window.height()/2,n=Math.ceil(s.$target.offset().top-t+s.$next_tip.outerHeight()),e("html, body").stop().animate({scrollTop:n},s.scrollSpeed)},paused:function(){return e.inArray(s.$li.index()+1,s.pauseAfter)===-1?!0:!1},destroy:function(){e.isEmptyObject(s)||s.$document.off(".joyride"),e(t).off(".joyride"),e(".joyride-close-tip, .joyride-next-tip, .joyride-modal-bg").off(".joyride"),e(".joyride-tip-guide, .joyride-modal-bg").remove(),clearTimeout(s.automate),s={}},restart:function(){s.autoStart?(o.hide(),s.$li=n,o.show("init")):(!s.startTimerOnClick&&s.timer>0?(o.show("init"),o.startTimer()):o.show("init"),s.autoStart=!0)},pos_default:function(t){var n=Math.ceil(s.$window.height()/2),r=s.$next_tip.offset(),i=e(".joyride-nub",s.$next_tip),u=Math.ceil(i.outerWidth()/2),a=Math.ceil(i.outerHeight()/2),f=t||!1;f&&(s.$next_tip.css("visibility","hidden"),s.$next_tip.show());if(!/body/i.test(s.$target.selector)){var l=s.tipSettings.tipAdjustmentY?parseInt(s.tipSettings.tipAdjustmentY):0,c=s.tipSettings.tipAdjustmentX?parseInt(s.tipSettings.tipAdjustmentX):0;o.bottom()?(s.$next_tip.css({top:s.$target.offset().top+a+s.$target.outerHeight()+l,left:s.$target.offset().left+c}),/right/i.test(s.tipSettings.nubPosition)&&s.$next_tip.css("left",s.$target.offset().left-s.$next_tip.outerWidth()+s.$target.outerWidth()),o.nub_position(i,s.tipSettings.nubPosition,"top")):o.top()?(s.$next_tip.css({top:s.$target.offset().top-s.$next_tip.outerHeight()-a+l,left:s.$target.offset().left+c}),o.nub_position(i,s.tipSettings.nubPosition,"bottom")):o.right()?(s.$next_tip.css({top:s.$target.offset().top+l,left:s.$target.outerWidth()+s.$target.offset().left+u+c}),o.nub_position(i,s.tipSettings.nubPosition,"left")):o.left()&&(s.$next_tip.css({top:s.$target.offset().top+l,left:s.$target.offset().left-s.$next_tip.outerWidth()-u+c}),o.nub_position(i,s.tipSettings.nubPosition,"right")),!o.visible(o.corners(s.$next_tip))&&s.attempts<s.tipSettings.tipLocationPattern.length&&(i.removeClass("bottom").removeClass("top").removeClass("right").removeClass("left"),s.tipSettings.tipLocation=s.tipSettings.tipLocationPattern[s.attempts],s.attempts++,o.pos_default(!0))}else s.$li.length&&o.pos_modal(i);f&&(s.$next_tip.hide(),s.$next_tip.css("visibility","visible"))},pos_phone:function(t){var n=s.$next_tip.outerHeight(),r=s.$next_tip.offset(),i=s.$target.outerHeight(),u=e(".joyride-nub",s.$next_tip),a=Math.ceil(u.outerHeight()/2),f=t||!1;u.removeClass("bottom").removeClass("top").removeClass("right").removeClass("left"),f&&(s.$next_tip.css("visibility","hidden"),s.$next_tip.show()),/body/i.test(s.$target.selector)?s.$li.length&&o.pos_modal(u):o.top()?(s.$next_tip.offset({top:s.$target.offset().top-n-a}),u.addClass("bottom")):(s.$next_tip.offset({top:s.$target.offset().top+i+a}),u.addClass("top")),f&&(s.$next_tip.hide(),s.$next_tip.css("visibility","visible"))},pos_modal:function(e){o.center(),e.hide(),o.show_modal()},show_modal:function(){e(".joyride-modal-bg").length<1&&e("body").append(s.template.modal).show(),/pop/i.test(s.tipAnimation)?e(".joyride-modal-bg").show():e(".joyride-modal-bg").fadeIn(s.tipAnimationFadeSpeed)},expose:function(){var n,r,i,u,a="expose-"+Math.floor(Math.random()*1e4);if(arguments.length>0&&arguments[0]instanceof e)i=arguments[0];else{if(!s.$target||!!/body/i.test(s.$target.selector))return!1;i=s.$target}if(i.length<1)return t.console&&console.error("element not valid",i),!1;n=e(s.template.expose),s.$body.append(n),n.css({top:i.offset().top,left:i.offset().left,width:i.outerWidth(!0),height:i.outerHeight(!0)}),r=e(s.template.exposeCover),u={zIndex:i.css("z-index"),position:i.css("position")},i.css("z-index",n.css("z-index")*1+1),u.position=="static"&&i.css("position","relative"),i.data("expose-css",u),r.css({top:i.offset().top,left:i.offset().left,width:i.outerWidth(!0),height:i.outerHeight(!0)}),s.$body.append(r),n.addClass(a),r.addClass(a),s.tipSettings.exposeClass&&(n.addClass(s.tipSettings.exposeClass),r.addClass(s.tipSettings.exposeClass)),i.data("expose",a),s.postExposeCallback(s.$li.index(),s.$next_tip,i),o.add_exposed(i)},un_expose:function(){var n,r,i,u,a=!1;if(arguments.length>0&&arguments[0]instanceof e)r=arguments[0];else{if(!s.$target||!!/body/i.test(s.$target.selector))return!1;r=s.$target}if(r.length<1)return t.console&&console.error("element not valid",r),!1;n=r.data("expose"),i=e("."+n),arguments.length>1&&(a=arguments[1]),a===!0?e(".joyride-expose-wrapper,.joyride-expose-cover").remove():i.remove(),u=r.data("expose-css"),u.zIndex=="auto"?r.css("z-index",""):r.css("z-index",u.zIndex),u.position!=r.css("position")&&(u.position=="static"?r.css("position",""):r.css("position",u.position)),r.removeData("expose"),r.removeData("expose-z-index"),o.remove_exposed(r)},add_exposed:function(t){s.exposed=s.exposed||[],t instanceof e?s.exposed.push(t[0]):typeof t=="string"&&s.exposed.push(t)},remove_exposed:function(t){var n;t instanceof e?n=t[0]:typeof t=="string"&&(n=t),s.exposed=s.exposed||[];for(var r=0;r<s.exposed.length;r++)if(s.exposed[r]==n){s.exposed.splice(r,1);return}},center:function(){var e=s.$window;return s.$next_tip.css({top:(e.height()-s.$next_tip.outerHeight())/2+e.scrollTop(),left:(e.width()-s.$next_tip.outerWidth())/2+e.scrollLeft()}),!0},bottom:function(){return/bottom/i.test(s.tipSettings.tipLocation)},top:function(){return/top/i.test(s.tipSettings.tipLocation)},right:function(){return/right/i.test(s.tipSettings.tipLocation)},left:function(){return/left/i.test(s.tipSettings.tipLocation)},corners:function(e){var t=s.$window,n=t.height()/2,r=Math.ceil(s.$target.offset().top-n+s.$next_tip.outerHeight()),i=t.width()+t.scrollLeft(),o=t.height()+r,u=t.height()+t.scrollTop(),a=t.scrollTop();return r<a&&(r<0?a=0:a=r),o>u&&(u=o),[e.offset().top<a,i<e.offset().left+e.outerWidth(),u<e.offset().top+e.outerHeight(),t.scrollLeft()>e.offset().left]},visible:function(e){var t=e.length;while(t--)if(e[t])return!1;return!0},nub_position:function(e,t,n){t==="auto"?e.addClass(n):e.addClass(t)},startTimer:function(){s.$li.length?s.automate=setTimeout(function(){o.hide(),o.show(),o.startTimer()},s.timer):clearTimeout(s.automate)},end:function(){s.cookieMonster&&e.cookie(s.cookieName,"ridden",{expires:365,domain:s.cookieDomain,path:s.cookiePath}),s.localStorage&&localStorage.setItem(s.localStorageKey,!0),s.timer>0&&clearTimeout(s.automate),s.modal&&s.expose&&o.un_expose(),s.$current_tip&&s.$current_tip.hide(),s.$li&&(s.postStepCallback(s.$li.index(),s.$current_tip),s.postRideCallback(s.$li.index(),s.$current_tip)),e(".joyride-modal-bg").hide()},jquery_check:function(){return e.isFunction(e.fn.on)?!0:(e.fn.on=function(e,t,n){return this.delegate(t,e,n)},e.fn.off=function(e,t,n){return this.undelegate(t,e,n)},!1)},outerHTML:function(e){return e.outerHTML||(new XMLSerializer).serializeToString(e)},version:function(){return s.version},tabbable:function(t){e(t).on("keydown",function(n){if(!n.isDefaultPrevented()&&n.keyCode&&n.keyCode===27){n.preventDefault(),o.end();return}if(n.keyCode!==9)return;var r=e(t).find(":tabbable"),i=r.filter(":first"),s=r.filter(":last");n.target===s[0]&&!n.shiftKey?(i.focus(1),n.preventDefault()):n.target===i[0]&&n.shiftKey&&(s.focus(1),n.preventDefault())})}};e.fn.joyride=function(t){if(o[t])return o[t].apply(this,Array.prototype.slice.call(arguments,1));if(typeof t=="object"||!t)return o.init.apply(this,arguments);e.error("Method "+t+" does not exist on jQuery.joyride")}})(jQuery,this);
  ;
  /**
   * @file
   * Attaches behaviors for the Tour module's toolbar tab.
   */
  
  (function ($, Backbone, Drupal, document) {
  
    'use strict';
  
    var queryString = decodeURI(window.location.search);
  
    /**
     * Attaches the tour's toolbar tab behavior.
     *
     * It uses the query string for:
     * - tour: When ?tour=1 is present, the tour will start automatically after
     *   the page has loaded.
     * - tips: Pass ?tips=class in the url to filter the available tips to the
     *   subset which match the given class.
     *
     * @example
     * http://example.com/foo?tour=1&tips=bar
     *
     * @type {Drupal~behavior}
     *
     * @prop {Drupal~behaviorAttach} attach
     *   Attach tour functionality on `tour` events.
     */
    Drupal.behaviors.tour = {
      attach: function (context) {
        $('body').once('tour').each(function () {
          var model = new Drupal.tour.models.StateModel();
          new Drupal.tour.views.ToggleTourView({
            el: $(context).find('#toolbar-tab-tour'),
            model: model
          });
  
          model
            // Allow other scripts to respond to tour events.
            .on('change:isActive', function (model, isActive) {
              $(document).trigger((isActive) ? 'drupalTourStarted' : 'drupalTourStopped');
            })
            // Initialization: check whether a tour is available on the current
            // page.
            .set('tour', $(context).find('ol#tour'));
  
          // Start the tour immediately if toggled via query string.
          if (/tour=?/i.test(queryString)) {
            model.set('isActive', true);
          }
        });
      }
    };
  
    /**
     * @namespace
     */
    Drupal.tour = Drupal.tour || {
  
      /**
       * @namespace Drupal.tour.models
       */
      models: {},
  
      /**
       * @namespace Drupal.tour.views
       */
      views: {}
    };
  
    /**
     * Backbone Model for tours.
     *
     * @constructor
     *
     * @augments Backbone.Model
     */
    Drupal.tour.models.StateModel = Backbone.Model.extend(/** @lends Drupal.tour.models.StateModel# */{
  
      /**
       * @type {object}
       */
      defaults: /** @lends Drupal.tour.models.StateModel# */{
  
        /**
         * Indicates whether the Drupal root window has a tour.
         *
         * @type {Array}
         */
        tour: [],
  
        /**
         * Indicates whether the tour is currently running.
         *
         * @type {bool}
         */
        isActive: false,
  
        /**
         * Indicates which tour is the active one (necessary to cleanly stop).
         *
         * @type {Array}
         */
        activeTour: []
      }
    });
  
    Drupal.tour.views.ToggleTourView = Backbone.View.extend(/** @lends Drupal.tour.views.ToggleTourView# */{
  
      /**
       * @type {object}
       */
      events: {click: 'onClick'},
  
      /**
       * Handles edit mode toggle interactions.
       *
       * @constructs
       *
       * @augments Backbone.View
       */
      initialize: function () {
        this.listenTo(this.model, 'change:tour change:isActive', this.render);
        this.listenTo(this.model, 'change:isActive', this.toggleTour);
      },
  
      /**
       * @inheritdoc
       *
       * @return {Drupal.tour.views.ToggleTourView}
       *   The `ToggleTourView` view.
       */
      render: function () {
        // Render the visibility.
        this.$el.toggleClass('hidden', this._getTour().length === 0);
        // Render the state.
        var isActive = this.model.get('isActive');
        this.$el.find('button')
          .toggleClass('is-active', isActive)
          .prop('aria-pressed', isActive);
        return this;
      },
  
      /**
       * Model change handler; starts or stops the tour.
       */
      toggleTour: function () {
        if (this.model.get('isActive')) {
          var $tour = this._getTour();
          this._removeIrrelevantTourItems($tour, this._getDocument());
          var that = this;
          if ($tour.find('li').length) {
            $tour.joyride({
              autoStart: true,
              postRideCallback: function () { that.model.set('isActive', false); },
              // HTML segments for tip layout.
              template: {
                link: '<a href=\"#close\" class=\"joyride-close-tip\">&times;</a>',
                button: '<a href=\"#\" class=\"button button--primary joyride-next-tip\"></a>'
              }
            });
            this.model.set({isActive: true, activeTour: $tour});
          }
        }
        else {
          this.model.get('activeTour').joyride('destroy');
          this.model.set({isActive: false, activeTour: []});
        }
      },
  
      /**
       * Toolbar tab click event handler; toggles isActive.
       *
       * @param {jQuery.Event} event
       *   The click event.
       */
      onClick: function (event) {
        this.model.set('isActive', !this.model.get('isActive'));
        event.preventDefault();
        event.stopPropagation();
      },
  
      /**
       * Gets the tour.
       *
       * @return {jQuery}
       *   A jQuery element pointing to a `<ol>` containing tour items.
       */
      _getTour: function () {
        return this.model.get('tour');
      },
  
      /**
       * Gets the relevant document as a jQuery element.
       *
       * @return {jQuery}
       *   A jQuery element pointing to the document within which a tour would be
       *   started given the current state.
       */
      _getDocument: function () {
        return $(document);
      },
  
      /**
       * Removes tour items for elements that don't have matching page elements.
       *
       * Or that are explicitly filtered out via the 'tips' query string.
       *
       * @example
       * <caption>This will filter out tips that do not have a matching
       * page element or don't have the "bar" class.</caption>
       * http://example.com/foo?tips=bar
       *
       * @param {jQuery} $tour
       *   A jQuery element pointing to a `<ol>` containing tour items.
       * @param {jQuery} $document
       *   A jQuery element pointing to the document within which the elements
       *   should be sought.
       *
       * @see Drupal.tour.views.ToggleTourView#_getDocument
       */
      _removeIrrelevantTourItems: function ($tour, $document) {
        var removals = false;
        var tips = /tips=([^&]+)/.exec(queryString);
        $tour
          .find('li')
          .each(function () {
            var $this = $(this);
            var itemId = $this.attr('data-id');
            var itemClass = $this.attr('data-class');
            // If the query parameter 'tips' is set, remove all tips that don't
            // have the matching class.
            if (tips && !$(this).hasClass(tips[1])) {
              removals = true;
              $this.remove();
              return;
            }
            // Remove tip from the DOM if there is no corresponding page element.
            if ((!itemId && !itemClass) ||
              (itemId && $document.find('#' + itemId).length) ||
              (itemClass && $document.find('.' + itemClass).length)) {
              return;
            }
            removals = true;
            $this.remove();
          });
  
        // If there were removals, we'll have to do some clean-up.
        if (removals) {
          var total = $tour.find('li').length;
          if (!total) {
            this.model.set({tour: []});
          }
  
          $tour
            .find('li')
            // Rebuild the progress data.
            .each(function (index) {
              var progress = Drupal.t('!tour_item of !total', {'!tour_item': index + 1, '!total': total});
              $(this).find('.tour-progress').text(progress);
            })
            // Update the last item to have "End tour" as the button.
            .eq(-1)
            .attr('data-text', Drupal.t('End tour'));
        }
      }
  
    });
  
  })(jQuery, Backbone, Drupal, document);
  ;
  /**
   * @file
   * Manages page tabbing modifications made by modules.
   */
  
  /**
   * Allow modules to respond to the constrain event.
   *
   * @event drupalTabbingConstrained
   */
  
  /**
   * Allow modules to respond to the tabbingContext release event.
   *
   * @event drupalTabbingContextReleased
   */
  
  /**
   * Allow modules to respond to the constrain event.
   *
   * @event drupalTabbingContextActivated
   */
  
  /**
   * Allow modules to respond to the constrain event.
   *
   * @event drupalTabbingContextDeactivated
   */
  
  (function ($, Drupal) {
  
    'use strict';
  
    /**
     * Provides an API for managing page tabbing order modifications.
     *
     * @constructor Drupal~TabbingManager
     */
    function TabbingManager() {
  
      /**
       * Tabbing sets are stored as a stack. The active set is at the top of the
       * stack. We use a JavaScript array as if it were a stack; we consider the
       * first element to be the bottom and the last element to be the top. This
       * allows us to use JavaScript's built-in Array.push() and Array.pop()
       * methods.
       *
       * @type {Array.<Drupal~TabbingContext>}
       */
      this.stack = [];
    }
  
    /**
     * Add public methods to the TabbingManager class.
     */
    $.extend(TabbingManager.prototype, /** @lends Drupal~TabbingManager# */{
  
      /**
       * Constrain tabbing to the specified set of elements only.
       *
       * Makes elements outside of the specified set of elements unreachable via
       * the tab key.
       *
       * @param {jQuery} elements
       *   The set of elements to which tabbing should be constrained. Can also
       *   be a jQuery-compatible selector string.
       *
       * @return {Drupal~TabbingContext}
       *   The TabbingContext instance.
       *
       * @fires event:drupalTabbingConstrained
       */
      constrain: function (elements) {
        // Deactivate all tabbingContexts to prepare for the new constraint. A
        // tabbingContext instance will only be reactivated if the stack is
        // unwound to it in the _unwindStack() method.
        var il = this.stack.length;
        for (var i = 0; i < il; i++) {
          this.stack[i].deactivate();
        }
  
        // The "active tabbing set" are the elements tabbing should be constrained
        // to.
        var $elements = $(elements).find(':tabbable').addBack(':tabbable');
  
        var tabbingContext = new TabbingContext({
          // The level is the current height of the stack before this new
          // tabbingContext is pushed on top of the stack.
          level: this.stack.length,
          $tabbableElements: $elements
        });
  
        this.stack.push(tabbingContext);
  
        // Activates the tabbingContext; this will manipulate the DOM to constrain
        // tabbing.
        tabbingContext.activate();
  
        // Allow modules to respond to the constrain event.
        $(document).trigger('drupalTabbingConstrained', tabbingContext);
  
        return tabbingContext;
      },
  
      /**
       * Restores a former tabbingContext when an active one is released.
       *
       * The TabbingManager stack of tabbingContext instances will be unwound
       * from the top-most released tabbingContext down to the first non-released
       * tabbingContext instance. This non-released instance is then activated.
       */
      release: function () {
        // Unwind as far as possible: find the topmost non-released
        // tabbingContext.
        var toActivate = this.stack.length - 1;
        while (toActivate >= 0 && this.stack[toActivate].released) {
          toActivate--;
        }
  
        // Delete all tabbingContexts after the to be activated one. They have
        // already been deactivated, so their effect on the DOM has been reversed.
        this.stack.splice(toActivate + 1);
  
        // Get topmost tabbingContext, if one exists, and activate it.
        if (toActivate >= 0) {
          this.stack[toActivate].activate();
        }
      },
  
      /**
       * Makes all elements outside of the tabbingContext's set untabbable.
       *
       * Elements made untabbable have their original tabindex and autofocus
       * values stored so that they might be restored later when this
       * tabbingContext is deactivated.
       *
       * @param {Drupal~TabbingContext} tabbingContext
       *   The TabbingContext instance that has been activated.
       */
      activate: function (tabbingContext) {
        var $set = tabbingContext.$tabbableElements;
        var level = tabbingContext.level;
        // Determine which elements are reachable via tabbing by default.
        var $disabledSet = $(':tabbable')
          // Exclude elements of the active tabbing set.
          .not($set);
        // Set the disabled set on the tabbingContext.
        tabbingContext.$disabledElements = $disabledSet;
        // Record the tabindex for each element, so we can restore it later.
        var il = $disabledSet.length;
        for (var i = 0; i < il; i++) {
          this.recordTabindex($disabledSet.eq(i), level);
        }
        // Make all tabbable elements outside of the active tabbing set
        // unreachable.
        $disabledSet
          .prop('tabindex', -1)
          .prop('autofocus', false);
  
        // Set focus on an element in the tabbingContext's set of tabbable
        // elements. First, check if there is an element with an autofocus
        // attribute. Select the last one from the DOM order.
        var $hasFocus = $set.filter('[autofocus]').eq(-1);
        // If no element in the tabbable set has an autofocus attribute, select
        // the first element in the set.
        if ($hasFocus.length === 0) {
          $hasFocus = $set.eq(0);
        }
        $hasFocus.trigger('focus');
      },
  
      /**
       * Restores that tabbable state of a tabbingContext's disabled elements.
       *
       * Elements that were made untabbable have their original tabindex and
       * autofocus values restored.
       *
       * @param {Drupal~TabbingContext} tabbingContext
       *   The TabbingContext instance that has been deactivated.
       */
      deactivate: function (tabbingContext) {
        var $set = tabbingContext.$disabledElements;
        var level = tabbingContext.level;
        var il = $set.length;
        for (var i = 0; i < il; i++) {
          this.restoreTabindex($set.eq(i), level);
        }
      },
  
      /**
       * Records the tabindex and autofocus values of an untabbable element.
       *
       * @param {jQuery} $el
       *   The set of elements that have been disabled.
       * @param {number} level
       *   The stack level for which the tabindex attribute should be recorded.
       */
      recordTabindex: function ($el, level) {
        var tabInfo = $el.data('drupalOriginalTabIndices') || {};
        tabInfo[level] = {
          tabindex: $el[0].getAttribute('tabindex'),
          autofocus: $el[0].hasAttribute('autofocus')
        };
        $el.data('drupalOriginalTabIndices', tabInfo);
      },
  
      /**
       * Restores the tabindex and autofocus values of a reactivated element.
       *
       * @param {jQuery} $el
       *   The element that is being reactivated.
       * @param {number} level
       *   The stack level for which the tabindex attribute should be restored.
       */
      restoreTabindex: function ($el, level) {
        var tabInfo = $el.data('drupalOriginalTabIndices');
        if (tabInfo && tabInfo[level]) {
          var data = tabInfo[level];
          if (data.tabindex) {
            $el[0].setAttribute('tabindex', data.tabindex);
          }
          // If the element did not have a tabindex at this stack level then
          // remove it.
          else {
            $el[0].removeAttribute('tabindex');
          }
          if (data.autofocus) {
            $el[0].setAttribute('autofocus', 'autofocus');
          }
  
          // Clean up $.data.
          if (level === 0) {
            // Remove all data.
            $el.removeData('drupalOriginalTabIndices');
          }
          else {
            // Remove the data for this stack level and higher.
            var levelToDelete = level;
            while (tabInfo.hasOwnProperty(levelToDelete)) {
              delete tabInfo[levelToDelete];
              levelToDelete++;
            }
            $el.data('drupalOriginalTabIndices', tabInfo);
          }
        }
      }
    });
  
    /**
     * Stores a set of tabbable elements.
     *
     * This constraint can be removed with the release() method.
     *
     * @constructor Drupal~TabbingContext
     *
     * @param {object} options
     *   A set of initiating values
     * @param {number} options.level
     *   The level in the TabbingManager's stack of this tabbingContext.
     * @param {jQuery} options.$tabbableElements
     *   The DOM elements that should be reachable via the tab key when this
     *   tabbingContext is active.
     * @param {jQuery} options.$disabledElements
     *   The DOM elements that should not be reachable via the tab key when this
     *   tabbingContext is active.
     * @param {bool} options.released
     *   A released tabbingContext can never be activated again. It will be
     *   cleaned up when the TabbingManager unwinds its stack.
     * @param {bool} options.active
     *   When true, the tabbable elements of this tabbingContext will be reachable
     *   via the tab key and the disabled elements will not. Only one
     *   tabbingContext can be active at a time.
     */
    function TabbingContext(options) {
  
      $.extend(this, /** @lends Drupal~TabbingContext# */{
  
        /**
         * @type {?number}
         */
        level: null,
  
        /**
         * @type {jQuery}
         */
        $tabbableElements: $(),
  
        /**
         * @type {jQuery}
         */
        $disabledElements: $(),
  
        /**
         * @type {bool}
         */
        released: false,
  
        /**
         * @type {bool}
         */
        active: false
      }, options);
    }
  
    /**
     * Add public methods to the TabbingContext class.
     */
    $.extend(TabbingContext.prototype, /** @lends Drupal~TabbingContext# */{
  
      /**
       * Releases this TabbingContext.
       *
       * Once a TabbingContext object is released, it can never be activated
       * again.
       *
       * @fires event:drupalTabbingContextReleased
       */
      release: function () {
        if (!this.released) {
          this.deactivate();
          this.released = true;
          Drupal.tabbingManager.release(this);
          // Allow modules to respond to the tabbingContext release event.
          $(document).trigger('drupalTabbingContextReleased', this);
        }
      },
  
      /**
       * Activates this TabbingContext.
       *
       * @fires event:drupalTabbingContextActivated
       */
      activate: function () {
        // A released TabbingContext object can never be activated again.
        if (!this.active && !this.released) {
          this.active = true;
          Drupal.tabbingManager.activate(this);
          // Allow modules to respond to the constrain event.
          $(document).trigger('drupalTabbingContextActivated', this);
        }
      },
  
      /**
       * Deactivates this TabbingContext.
       *
       * @fires event:drupalTabbingContextDeactivated
       */
      deactivate: function () {
        if (this.active) {
          this.active = false;
          Drupal.tabbingManager.deactivate(this);
          // Allow modules to respond to the constrain event.
          $(document).trigger('drupalTabbingContextDeactivated', this);
        }
      }
    });
  
    // Mark this behavior as processed on the first pass and return if it is
    // already processed.
    if (Drupal.tabbingManager) {
      return;
    }
  
    /**
     * @type {Drupal~TabbingManager}
     */
    Drupal.tabbingManager = new TabbingManager();
  
  }(jQuery, Drupal));
  ;
  /**
   * @file
   * Attaches behaviors for the Contextual module's edit toolbar tab.
   */
  
  (function ($, Drupal, Backbone) {
  
    'use strict';
  
    var strings = {
      tabbingReleased: Drupal.t('Tabbing is no longer constrained by the Contextual module.'),
      tabbingConstrained: Drupal.t('Tabbing is constrained to a set of @contextualsCount and the edit mode toggle.'),
      pressEsc: Drupal.t('Press the esc key to exit.')
    };
  
    /**
     * Initializes a contextual link: updates its DOM, sets up model and views.
     *
     * @param {HTMLElement} context
     *   A contextual links DOM element as rendered by the server.
     */
    function initContextualToolbar(context) {
      if (!Drupal.contextual || !Drupal.contextual.collection) {
        return;
      }
  
      var contextualToolbar = Drupal.contextualToolbar;
      var model = contextualToolbar.model = new contextualToolbar.StateModel({
        // Checks whether localStorage indicates we should start in edit mode
        // rather than view mode.
        // @see Drupal.contextualToolbar.VisualView.persist
        isViewing: localStorage.getItem('Drupal.contextualToolbar.isViewing') !== 'false'
      }, {
        contextualCollection: Drupal.contextual.collection
      });
  
      var viewOptions = {
        el: $('.toolbar .toolbar-bar .contextual-toolbar-tab'),
        model: model,
        strings: strings
      };
      new contextualToolbar.VisualView(viewOptions);
      new contextualToolbar.AuralView(viewOptions);
    }
  
    /**
     * Attaches contextual's edit toolbar tab behavior.
     *
     * @type {Drupal~behavior}
     *
     * @prop {Drupal~behaviorAttach} attach
     *   Attaches contextual toolbar behavior on a contextualToolbar-init event.
     */
    Drupal.behaviors.contextualToolbar = {
      attach: function (context) {
        if ($('body').once('contextualToolbar-init').length) {
          initContextualToolbar(context);
        }
      }
    };
  
    /**
     * Namespace for the contextual toolbar.
     *
     * @namespace
     */
    Drupal.contextualToolbar = {
  
      /**
       * The {@link Drupal.contextualToolbar.StateModel} instance.
       *
       * @type {?Drupal.contextualToolbar.StateModel}
       */
      model: null
    };
  
  })(jQuery, Drupal, Backbone);
  ;
  /**
   * @file
   * A Backbone Model for the state of Contextual module's edit toolbar tab.
   */
  
  (function (Drupal, Backbone) {
  
    'use strict';
  
    Drupal.contextualToolbar.StateModel = Backbone.Model.extend(/** @lends Drupal.contextualToolbar.StateModel# */{
  
      /**
       * @type {object}
       *
       * @prop {bool} isViewing
       * @prop {bool} isVisible
       * @prop {number} contextualCount
       * @prop {Drupal~TabbingContext} tabbingContext
       */
      defaults: /** @lends Drupal.contextualToolbar.StateModel# */{
  
        /**
         * Indicates whether the toggle is currently in "view" or "edit" mode.
         *
         * @type {bool}
         */
        isViewing: true,
  
        /**
         * Indicates whether the toggle should be visible or hidden. Automatically
         * calculated, depends on contextualCount.
         *
         * @type {bool}
         */
        isVisible: false,
  
        /**
         * Tracks how many contextual links exist on the page.
         *
         * @type {number}
         */
        contextualCount: 0,
  
        /**
         * A TabbingContext object as returned by {@link Drupal~TabbingManager}:
         * the set of tabbable elements when edit mode is enabled.
         *
         * @type {?Drupal~TabbingContext}
         */
        tabbingContext: null
      },
  
      /**
       * Models the state of the edit mode toggle.
       *
       * @constructs
       *
       * @augments Backbone.Model
       *
       * @param {object} attrs
       *   Attributes for the backbone model.
       * @param {object} options
       *   An object with the following option:
       * @param {Backbone.collection} options.contextualCollection
       *   The collection of {@link Drupal.contextual.StateModel} models that
       *   represent the contextual links on the page.
       */
      initialize: function (attrs, options) {
        // Respond to new/removed contextual links.
        this.listenTo(options.contextualCollection, 'reset remove add', this.countContextualLinks);
        this.listenTo(options.contextualCollection, 'add', this.lockNewContextualLinks);
  
        // Automatically determine visibility.
        this.listenTo(this, 'change:contextualCount', this.updateVisibility);
  
        // Whenever edit mode is toggled, lock all contextual links.
        this.listenTo(this, 'change:isViewing', function (model, isViewing) {
          options.contextualCollection.each(function (contextualModel) {
            contextualModel.set('isLocked', !isViewing);
          });
        });
      },
  
      /**
       * Tracks the number of contextual link models in the collection.
       *
       * @param {Drupal.contextual.StateModel} contextualModel
       *   The contextual links model that was added or removed.
       * @param {Backbone.Collection} contextualCollection
       *    The collection of contextual link models.
       */
      countContextualLinks: function (contextualModel, contextualCollection) {
        this.set('contextualCount', contextualCollection.length);
      },
  
      /**
       * Lock newly added contextual links if edit mode is enabled.
       *
       * @param {Drupal.contextual.StateModel} contextualModel
       *   The contextual links model that was added.
       * @param {Backbone.Collection} [contextualCollection]
       *    The collection of contextual link models.
       */
      lockNewContextualLinks: function (contextualModel, contextualCollection) {
        if (!this.get('isViewing')) {
          contextualModel.set('isLocked', true);
        }
      },
  
      /**
       * Automatically updates visibility of the view/edit mode toggle.
       */
      updateVisibility: function () {
        this.set('isVisible', this.get('contextualCount') > 0);
      }
  
    });
  
  })(Drupal, Backbone);
  ;
  /**
   * @file
   * A Backbone View that provides the aural view of the edit mode toggle.
   */
  
  (function ($, Drupal, Backbone, _) {
  
    'use strict';
  
    Drupal.contextualToolbar.AuralView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.AuralView# */{
  
      /**
       * Tracks whether the tabbing constraint announcement has been read once.
       *
       * @type {bool}
       */
      announcedOnce: false,
  
      /**
       * Renders the aural view of the edit mode toggle (screen reader support).
       *
       * @constructs
       *
       * @augments Backbone.View
       *
       * @param {object} options
       *   Options for the view.
       */
      initialize: function (options) {
        this.options = options;
  
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'change:isViewing', this.manageTabbing);
  
        $(document).on('keyup', _.bind(this.onKeypress, this));
      },
  
      /**
       * @inheritdoc
       *
       * @return {Drupal.contextualToolbar.AuralView}
       *   The current contextual toolbar aural view.
       */
      render: function () {
        // Render the state.
        this.$el.find('button').attr('aria-pressed', !this.model.get('isViewing'));
  
        return this;
      },
  
      /**
       * Limits tabbing to the contextual links and edit mode toolbar tab.
       */
      manageTabbing: function () {
        var tabbingContext = this.model.get('tabbingContext');
        // Always release an existing tabbing context.
        if (tabbingContext) {
          tabbingContext.release();
          Drupal.announce(this.options.strings.tabbingReleased);
        }
        // Create a new tabbing context when edit mode is enabled.
        if (!this.model.get('isViewing')) {
          tabbingContext = Drupal.tabbingManager.constrain($('.contextual-toolbar-tab, .contextual'));
          this.model.set('tabbingContext', tabbingContext);
          this.announceTabbingConstraint();
          this.announcedOnce = true;
        }
      },
  
      /**
       * Announces the current tabbing constraint.
       */
      announceTabbingConstraint: function () {
        var strings = this.options.strings;
        Drupal.announce(Drupal.formatString(strings.tabbingConstrained, {
          '@contextualsCount': Drupal.formatPlural(Drupal.contextual.collection.length, '@count contextual link', '@count contextual links')
        }));
        Drupal.announce(strings.pressEsc);
      },
  
      /**
       * Responds to esc and tab key press events.
       *
       * @param {jQuery.Event} event
       *   The keypress event.
       */
      onKeypress: function (event) {
        // The first tab key press is tracked so that an annoucement about tabbing
        // constraints can be raised if edit mode is enabled when the page is
        // loaded.
        if (!this.announcedOnce && event.keyCode === 9 && !this.model.get('isViewing')) {
          this.announceTabbingConstraint();
          // Set announce to true so that this conditional block won't run again.
          this.announcedOnce = true;
        }
        // Respond to the ESC key. Exit out of edit mode.
        if (event.keyCode === 27) {
          this.model.set('isViewing', true);
        }
      }
  
    });
  
  })(jQuery, Drupal, Backbone, _);
  ;
  /**
   * @file
   * A Backbone View that provides the visual view of the edit mode toggle.
   */
  
  (function (Drupal, Backbone) {
  
    'use strict';
  
    Drupal.contextualToolbar.VisualView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.VisualView# */{
  
      /**
       * Events for the Backbone view.
       *
       * @return {object}
       *   A mapping of events to be used in the view.
       */
      events: function () {
        // Prevents delay and simulated mouse events.
        var touchEndToClick = function (event) {
          event.preventDefault();
          event.target.click();
        };
  
        return {
          click: function () {
            this.model.set('isViewing', !this.model.get('isViewing'));
          },
          touchend: touchEndToClick
        };
      },
  
      /**
       * Renders the visual view of the edit mode toggle.
       *
       * Listens to mouse & touch and handles edit mode toggle interactions.
       *
       * @constructs
       *
       * @augments Backbone.View
       */
      initialize: function () {
        this.listenTo(this.model, 'change', this.render);
        this.listenTo(this.model, 'change:isViewing', this.persist);
      },
  
      /**
       * @inheritdoc
       *
       * @return {Drupal.contextualToolbar.VisualView}
       *   The current contextual toolbar visual view.
       */
      render: function () {
        // Render the visibility.
        this.$el.toggleClass('hidden', !this.model.get('isVisible'));
        // Render the state.
        this.$el.find('button').toggleClass('is-active', !this.model.get('isViewing'));
  
        return this;
      },
  
      /**
       * Model change handler; persists the isViewing value to localStorage.
       *
       * `isViewing === true` is the default, so only stores in localStorage when
       * it's not the default value (i.e. false).
       *
       * @param {Drupal.contextualToolbar.StateModel} model
       *   A {@link Drupal.contextualToolbar.StateModel} model.
       * @param {bool} isViewing
       *   The value of the isViewing attribute in the model.
       */
      persist: function (model, isViewing) {
        if (!isViewing) {
          localStorage.setItem('Drupal.contextualToolbar.isViewing', 'false');
        }
        else {
          localStorage.removeItem('Drupal.contextualToolbar.isViewing');
        }
      }
  
    });
  
  })(Drupal, Backbone);
  ;
  /**
   * @file
   * Replaces the home link in toolbar with a back to site link.
   */
  
  (function ($, Drupal, drupalSettings) {
  
    'use strict';
  
    var pathInfo = drupalSettings.path;
    var escapeAdminPath = sessionStorage.getItem('escapeAdminPath');
    var windowLocation = window.location;
  
    // Saves the last non-administrative page in the browser to be able to link
    // back to it when browsing administrative pages. If there is a destination
    // parameter there is not need to save the current path because the page is
    // loaded within an existing "workflow".
    if (!pathInfo.currentPathIsAdmin && !/destination=/.test(windowLocation.search)) {
      sessionStorage.setItem('escapeAdminPath', windowLocation);
    }
  
    /**
     * Replaces the "Home" link with "Back to site" link.
     *
     * Back to site link points to the last non-administrative page the user
     * visited within the same browser tab.
     *
     * @type {Drupal~behavior}
     *
     * @prop {Drupal~behaviorAttach} attach
     *   Attaches the replacement functionality to the toolbar-escape-admin element.
     */
    Drupal.behaviors.escapeAdmin = {
      attach: function () {
        var $toolbarEscape = $('[data-toolbar-escape-admin]').once('escapeAdmin');
        if ($toolbarEscape.length && pathInfo.currentPathIsAdmin) {
          if (escapeAdminPath !== null) {
            $toolbarEscape.attr('href', escapeAdminPath);
          }
          else {
            $toolbarEscape.text(Drupal.t('Home'));
          }
          $toolbarEscape.closest('.toolbar-tab').removeClass('hidden');
        }
      }
    };
  
  })(jQuery, Drupal, drupalSettings);
  ;
  