text: /*!
   * jQuery Once v2.2.0 - http://github.com/robloach/jquery-once
   * @license MIT, GPL-2.0
   *   http://opensource.org/licenses/MIT
   *   http://opensource.org/licenses/GPL-2.0
   */
  (function(e){"use strict";if(typeof exports==="object"){e(require("jquery"))}else if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e){"use strict";var n=function(e){e=e||"once";if(typeof e!=="string"){throw new TypeError("The jQuery Once id parameter must be a string")}return e};e.fn.once=function(t){var r="jquery-once-"+n(t);return this.filter(function(){return e(this).data(r)!==true}).data(r,true)};e.fn.removeOnce=function(e){return this.findOnce(e).removeData("jquery-once-"+n(e))};e.fn.findOnce=function(t){var r="jquery-once-"+n(t);return this.filter(function(){return e(this).data(r)===true})}});
  
  /**
  * DO NOT EDIT THIS FILE.
  * See the following change record for more information,
  * https://www.drupal.org/node/2815083
  * @preserve
  **/
  
  (function () {
    var settingsElement = document.querySelector('head > script[type="application/json"][data-drupal-selector="drupal-settings-json"], body > script[type="application/json"][data-drupal-selector="drupal-settings-json"]');
  
    window.drupalSettings = {};
  
    if (settingsElement !== null) {
      window.drupalSettings = JSON.parse(settingsElement.textContent);
    }
  })();;
  window.drupalTranslations = {"strings":{"":{"Save":"Salva","An AJAX HTTP error occurred.":"Si \u00e8 verificato un errore HTTP in AJAX.","HTTP Result Code: !status":"Codice HTTP di risposta: !status","An AJAX HTTP request terminated abnormally.":"Una richiesta AJAX HTTP \u00e8 terminata in modo anomalo.","Debugging information follows.":"Di seguito le informazioni di debug.","Path: !uri":"Percorso: !uri","StatusText: !statusText":"StatusText: !statusText","ResponseText: !responseText":"ResponseText: !responseText","ReadyState: !readyState":"ReadyState: !readyState","CustomMessage: !customMessage":"Messaggio personalizzato: !customMessage","Please wait...":"Attendere prego...","The response failed verification so will not be processed.":"La risposta non ha superato la verifica, quindi non verr\u00e0 elaborata.","The callback URL is not local and not trusted: !url":"L\u0027URL di callback non \u00e8 locale o non \u00e8 affidabile: !url","Changed":"Modificato","Home":"Home","Next":"Seguente","closed":"chiuso","Cancel":"Annulla","Disabled":"Disattivato","Enabled":"Attivato","Edit":"Modifica","Link":"Link","Image":"Immagine","Open":"Aperto","Sunday":"Domenica","Monday":"Luned\u00ec","Tuesday":"Marted\u00ec","Wednesday":"Mercoled\u00ec","Thursday":"Gioved\u00ec","Friday":"Venerd\u00ec","Saturday":"Sabato","Add":"Aggiungi","Continue":"Continua","Done":"Fatto","OK":"OK","Prev":"Precedente","Mon":"Lun","Tue":"Mar","Wed":"Mer","Thu":"Gio","Fri":"Ven","Sat":"Sab","Sun":"Dom","May":"Mag","Close":"Chiudi","Show":"Mostra","Select all rows in this table":"Seleziona tutte le righe in questa tabella","Deselect all rows in this table":"Deseleziona tutte le righe in questa tabella","Today":"Oggi","Jan":"Gen","Feb":"Feb","Mar":"Mar","Apr":"Apr","Jun":"Giu","Jul":"Lug","Aug":"Ago","Sep":"Set","Oct":"Ott","Nov":"Nov","Dec":"Dic","Extend":"Estendi","Su":"Do","Mo":"Lu","Tu":"Ma","We":"Noi","Th":"Gi","Fr":"Ve","Sa":"Sa","Not published":"Non pubblicato","Loading...":"In caricamento...","Hide":"Nascondi","Unlink":"Rimuovi link","Not promoted":"Non promosso","mm\/dd\/yy":"mm\/gg\/aa","Error message":"Messaggio di errore","Quick edit":"Modifica veloce","Edit Link":"Modifica link","Remove group":"Rimuovi gruppo","By @name on @date":"Da @name il @date","By @name":"Da @name","Not in menu":"Non nel menu","Alias: @alias":"Alias: @alias","No alias":"Nessun alias","@label":"@label","New revision":"Nuova revisione","Drag to re-order":"Trascina per riordinare","Discard changes":"Abbandona le modifiche","Breadcrumbs":"Breadcrumbs","Saving":"Salvataggio","This permission is inherited from the authenticated user role.":"Questo permesso viene ereditato dal ruolo utente autenticato.","No revision":"Nessuna revisione","Requires a title":"Richiede un titolo","Not restricted":"Non limitato","(active tab)":"(scheda attiva)","Restricted to certain pages":"Limitato a certe pagine","The block cannot be placed in this region.":"Il blocco non pu\u00f2 essere posizionato in questa regione.","Hide summary":"Nascondi sommario","Edit summary":"Modifica sommario","Don\u0027t display post information":"Non mostrare le informazioni di pubblicazione","Collapse":"Raggruppa","Show row weights":"Visualizza i pesi delle righe","Hide row weights":"Nascondi i pesi delle righe","Apply (all displays)":"Applica (tutte le visualizzazioni)","Apply (this display)":"Applica (solo questa visualizzazione)","Revert to default":"Ritorna al predefinito","You have unsaved changes":"Ci sono modifiche non salvate","Needs to be updated":"Deve essere aggiornato","Does not need to be updated":"Non ha bisogno di essere aggiornato","Show all columns":"Mostra tutte le colonne","Show table cells that were hidden to make the table fit within a small screen.":"Mostra le celle che sono state nascoste per adattare la tabella ad uno schermo piccolo.","List additional actions":"Mostra ulteriori azioni","Flag other translations as outdated":"Marca altre traduzioni come obsolete","Do not flag other translations as outdated":"Non marcare altre traduzioni come obsolete","opened":"aperto","Horizontal orientation":"Orientamento orizzontale","Vertical orientation":"Orientamento verticale","You have unsaved changes.":"Sono presenti modifiche non salvate.","@action @title configuration options":"Opzioni di configurazione per @action @title","Tabbing is no longer constrained by the Contextual module.":"L\u0027uso del tasto tab non \u00e8 pi\u00f9 vincolato al modulo Contextual links.","Tabbing is constrained to a set of @contextualsCount and the edit mode toggle.":"L\u0027uso del tasto tab \u00e8 vincolato a un gruppo di @contextualsCount e al passaggio dalla e alla modalit\u00e0 di modifica.","Press the esc key to exit.":"Premere il tasto ESC per uscire.","@count contextual link\u0003@count contextual links":"@count link contestuale\u0003@count link contestuali","!tour_item of !total":"!tour_item di !total","End tour":"Finisci il Tour","Discard changes?":"Annullare le modifiche?","The toolbar cannot be set to a horizontal orientation when it is locked.":"La barra degli strumenti non pu\u00f2 essere impostata su un orientamento orizzontale quando \u00e8 bloccata.","Could not load the form for \u003Cq\u003E@field-label\u003C\/q\u003E, either due to a website problem or a network connection problem.\u003Cbr\u003EPlease try again.":"Impossibile caricare il form per \u003Cq\u003E@field-label\u003C\/q\u003E, a causa di un problema del sito o di un problema di connessione di rete.\u003Cbr\u003ESi prega di riprovare.","Your changes to \u003Cq\u003E@entity-title\u003C\/q\u003E could not be saved, either due to a website problem or a network connection problem.\u003Cbr\u003EPlease try again.":"Le modifiche a \u003Cq\u003E@entity-title\u003C\/q\u003E non possono essere salvate, a causa di un problema del sito web o un problema di connessione di rete.\u003Cbr\u003ESi prega di riprovare.","Changing the text format to %text_format will permanently remove content that is not allowed in that text format.\u003Cbr\u003E\u003Cbr\u003ESave your changes before switching the text format to avoid losing data.":"Il cambio del formato di testo in %text_format comporter\u00e0 la perdita definitiva di tutti gli elementi non consentiti nel nuovo formato.\u003Cbr\u003E\u003Cbr\u003ESalvare i cambiamenti prima del cambio di formato per evitare la perdita di dati.","Change text format?":"Cambiare il formato del testo?","Rich Text Editor, !label field":"Editor di testo formattato, campo !label","Hide lower priority columns":"Nascondi colonne a priorit\u00e0 inferiore.","!modules modules are available in the modified list.":"I moduli !modules sono disponibili nell\u0027elenco modificato.","Network problem!":"Problema di rete!","Authored on @date":"Scritto il @date"},"Long month name":{"January":"Gennaio","February":"Febbraio","March":"Marzo","April":"Aprile","May":"Maggio","June":"Giugno","July":"Luglio","August":"Agosto","September":"Settembre","October":"Ottobre","November":"Novembre","December":"Dicembre"}},"pluralFormula":{"1":0,"default":1}};;
  /**
  * DO NOT EDIT THIS FILE.
  * See the following change record for more information,
  * https://www.drupal.org/node/2815083
  * @preserve
  **/
  
  window.Drupal = { behaviors: {}, locale: {} };
  
  (function (Drupal, drupalSettings, drupalTranslations, console, Proxy, Reflect) {
    Drupal.throwError = function (error) {
      setTimeout(function () {
        throw error;
      }, 0);
    };
  
    Drupal.attachBehaviors = function (context, settings) {
      context = context || document;
      settings = settings || drupalSettings;
      var behaviors = Drupal.behaviors;
  
      Object.keys(behaviors || {}).forEach(function (i) {
        if (typeof behaviors[i].attach === 'function') {
          try {
            behaviors[i].attach(context, settings);
          } catch (e) {
            Drupal.throwError(e);
          }
        }
      });
    };
  
    Drupal.detachBehaviors = function (context, settings, trigger) {
      context = context || document;
      settings = settings || drupalSettings;
      trigger = trigger || 'unload';
      var behaviors = Drupal.behaviors;
  
      Object.keys(behaviors || {}).forEach(function (i) {
        if (typeof behaviors[i].detach === 'function') {
          try {
            behaviors[i].detach(context, settings, trigger);
          } catch (e) {
            Drupal.throwError(e);
          }
        }
      });
    };
  
    Drupal.checkPlain = function (str) {
      str = str.toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
      return str;
    };
  
    Drupal.formatString = function (str, args) {
      var processedArgs = {};
  
      Object.keys(args || {}).forEach(function (key) {
        switch (key.charAt(0)) {
          case '@':
            processedArgs[key] = Drupal.checkPlain(args[key]);
            break;
  
          case '!':
            processedArgs[key] = args[key];
            break;
  
          default:
            processedArgs[key] = Drupal.theme('placeholder', args[key]);
            break;
        }
      });
  
      return Drupal.stringReplace(str, processedArgs, null);
    };
  
    Drupal.stringReplace = function (str, args, keys) {
      if (str.length === 0) {
        return str;
      }
  
      if (!Array.isArray(keys)) {
        keys = Object.keys(args || {});
  
        keys.sort(function (a, b) {
          return a.length - b.length;
        });
      }
  
      if (keys.length === 0) {
        return str;
      }
  
      var key = keys.pop();
      var fragments = str.split(key);
  
      if (keys.length) {
        for (var i = 0; i < fragments.length; i++) {
          fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0));
        }
      }
  
      return fragments.join(args[key]);
    };
  
    Drupal.t = function (str, args, options) {
      options = options || {};
      options.context = options.context || '';
  
      if (typeof drupalTranslations !== 'undefined' && drupalTranslations.strings && drupalTranslations.strings[options.context] && drupalTranslations.strings[options.context][str]) {
        str = drupalTranslations.strings[options.context][str];
      }
  
      if (args) {
        str = Drupal.formatString(str, args);
      }
      return str;
    };
  
    Drupal.url = function (path) {
      return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
    };
  
    Drupal.url.toAbsolute = function (url) {
      var urlParsingNode = document.createElement('a');
  
      try {
        url = decodeURIComponent(url);
      } catch (e) {}
  
      urlParsingNode.setAttribute('href', url);
  
      return urlParsingNode.cloneNode(false).href;
    };
  
    Drupal.url.isLocal = function (url) {
      var absoluteUrl = Drupal.url.toAbsolute(url);
      var protocol = window.location.protocol;
  
      if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) {
        protocol = 'https:';
      }
      var baseUrl = protocol + '//' + window.location.host + drupalSettings.path.baseUrl.slice(0, -1);
  
      try {
        absoluteUrl = decodeURIComponent(absoluteUrl);
      } catch (e) {}
      try {
        baseUrl = decodeURIComponent(baseUrl);
      } catch (e) {}
  
      return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0;
    };
  
    Drupal.formatPlural = function (count, singular, plural, args, options) {
      args = args || {};
      args['@count'] = count;
  
      var pluralDelimiter = drupalSettings.pluralDelimiter;
      var translations = Drupal.t(singular + pluralDelimiter + plural, args, options).split(pluralDelimiter);
      var index = 0;
  
      if (typeof drupalTranslations !== 'undefined' && drupalTranslations.pluralFormula) {
        index = count in drupalTranslations.pluralFormula ? drupalTranslations.pluralFormula[count] : drupalTranslations.pluralFormula.default;
      } else if (args['@count'] !== 1) {
        index = 1;
      }
  
      return translations[index];
    };
  
    Drupal.encodePath = function (item) {
      return window.encodeURIComponent(item).replace(/%2F/g, '/');
    };
  
    Drupal.deprecationError = function (_ref) {
      var message = _ref.message;
  
      if (drupalSettings.suppressDeprecationErrors === false && typeof console !== 'undefined' && console.warn) {
        console.warn('[Deprecation] ' + message);
      }
    };
  
    Drupal.deprecatedProperty = function (_ref2) {
      var target = _ref2.target,
          deprecatedProperty = _ref2.deprecatedProperty,
          message = _ref2.message;
  
      if (!Proxy || !Reflect) {
        return target;
      }
  
      return new Proxy(target, {
        get: function get(target, key) {
          for (var _len = arguments.length, rest = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
            rest[_key - 2] = arguments[_key];
          }
  
          if (key === deprecatedProperty) {
            Drupal.deprecationError({ message: message });
          }
          return Reflect.get.apply(Reflect, [target, key].concat(rest));
        }
      });
    };
  
    Drupal.theme = function (func) {
      if (func in Drupal.theme) {
        var _Drupal$theme;
  
        for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
          args[_key2 - 1] = arguments[_key2];
        }
  
        return (_Drupal$theme = Drupal.theme)[func].apply(_Drupal$theme, args);
      }
    };
  
    Drupal.theme.placeholder = function (str) {
      return '<em class="placeholder">' + Drupal.checkPlain(str) + '</em>';
    };
  })(Drupal, window.drupalSettings, window.drupalTranslations, window.console, window.Proxy, window.Reflect);;
  /**
  * DO NOT EDIT THIS FILE.
  * See the following change record for more information,
  * https://www.drupal.org/node/2815083
  * @preserve
  **/
  
  if (window.jQuery) {
    jQuery.noConflict();
  }
  
  document.documentElement.className += ' js';
  
  (function (Drupal, drupalSettings) {
    var domReady = function domReady(callback) {
      if (document.readyState !== 'loading') {
        callback();
      } else {
        var listener = function listener() {
          callback();
          document.removeEventListener('DOMContentLoaded', listener);
        };
        document.addEventListener('DOMContentLoaded', listener);
      }
    };
  
    domReady(function () {
      Drupal.attachBehaviors(document, drupalSettings);
    });
  })(Drupal, window.drupalSettings);;
  /**
   * @file
   * Drupal Bootstrap object.
   */
  
  /**
   * All Drupal Bootstrap JavaScript APIs are contained in this namespace.
   *
   * @namespace
   */
  (function ($, Drupal, drupalSettings) {
    'use strict';
  
    Drupal.bootstrap = {
      settings: drupalSettings.bootstrap || {}
    };
  
    /**
     * Wraps Drupal.checkPlain() to ensure value passed isn't empty.
     *
     * Encodes special characters in a plain-text string for display as HTML.
     *
     * @param {string} str
     *   The string to be encoded.
     *
     * @return {string}
     *   The encoded string.
     *
     * @ingroup sanitization
     */
    Drupal.bootstrap.checkPlain = function (str) {
      return str && Drupal.checkPlain(str) || '';
    };
  
    /**
     * Extends a Bootstrap plugin constructor.
     *
     * @param {string} id
     *   A Bootstrap plugin identifier located in $.fn.
     * @param {function} [callback]
     *   A callback to extend the plugin constructor.
     *
     * @return {function|boolean}
     *   The Bootstrap plugin or FALSE if the plugin does not exist.
     */
    Drupal.bootstrap.extendPlugin = function (id, callback) {
      // Immediately return if the plugin does not exist.
      if (!$.fn[id] || !$.fn[id].Constructor) return false;
  
      // Extend the plugin if a callback was provided.
      if ($.isFunction(callback)) {
        var ret = callback.apply($.fn[id].Constructor, [this.settings]);
        if ($.isPlainObject(ret)) {
          $.extend(true, $.fn[id].Constructor, ret);
        }
      }
  
      // Add a jQuery UI like option getter/setter method.
      if ($.fn[id].Constructor.prototype.option === void(0)) {
        $.fn[id].Constructor.prototype.option = this.option;
      }
  
      return $.fn[id].Constructor;
    };
  
    /**
     * Replaces a Bootstrap jQuery plugin definition.
     *
     * @param {string} id
     *   A Bootstrap plugin identifier located in $.fn.
     * @param {function} [callback]
     *   A callback to replace the jQuery plugin definition. The callback must
     *   return a function that is used to construct a jQuery plugin.
     *
     * @return {function|boolean}
     *   The Bootstrap jQuery plugin definition or FALSE if the plugin does not
     *   exist.
     */
    Drupal.bootstrap.replacePlugin = function (id, callback) {
      // Immediately return if plugin does not exist or not a valid callback.
      if (!$.fn[id] || !$.fn[id].Constructor || !$.isFunction(callback)) return false;
      var constructor = $.fn[id].Constructor;
  
      var plugin = callback.apply(constructor);
      if ($.isFunction(plugin)) {
        plugin.Constructor = constructor;
  
        var old = $.fn[id];
        plugin.noConflict = function () {
          $.fn[id] = old;
          return this;
        };
        $.fn[id] = plugin;
      }
    };
  
    /**
     * Map of supported events by regular expression.
     *
     * @type {Object<Event|MouseEvent|KeyboardEvent|TouchEvent,RegExp>}
     */
    Drupal.bootstrap.eventMap = {
      Event: /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
      MouseEvent: /^(?:click|dblclick|mouse(?:down|enter|leave|up|over|move|out))$/,
      KeyboardEvent: /^(?:key(?:down|press|up))$/,
      TouchEvent: /^(?:touch(?:start|end|move|cancel))$/
    };
  
    /**
     * Simulates a native event on an element in the browser.
     *
     * Note: This is a pretty complete modern implementation. If things are quite
     * working the way you intend (in older browsers), you may wish to use the
     * jQuery.simulate plugin. If it's available, this method will defer to it.
     *
     * @see https://github.com/jquery/jquery-simulate
     *
     * @param {HTMLElement} element
     *   A DOM element to dispatch event on.
     * @param {String} type
     *   The type of event to simulate.
     * @param {Object} [options]
     *   An object of options to pass to the event constructor. Typically, if
     *   an event is being proxied, you should just pass the original event
     *   object here. This allows, if the browser supports it, to be a truly
     *   simulated event.
     */
    Drupal.bootstrap.simulate = function (element, type, options) {
      // Defer to the jQuery.simulate plugin, if it's available.
      if (typeof $.simulate === 'function') {
        new $.simulate(element, type, options);
        return;
      }
      var event;
      var ctor;
      for (var name in Drupal.bootstrap.eventMap) {
        if (Drupal.bootstrap.eventMap[name].test(type)) {
          ctor = name;
          break;
        }
      }
      if (!ctor) {
        throw new SyntaxError('Only rudimentary HTMLEvents, KeyboardEvents and MouseEvents are supported: ' + type);
      }
      var opts = {bubbles: true, cancelable: true};
      if (ctor === 'KeyboardEvent' || ctor === 'MouseEvent') {
        $.extend(opts, {ctrlKey: !1, altKey: !1, shiftKey: !1, metaKey: !1});
      }
      if (ctor === 'MouseEvent') {
        $.extend(opts, {button: 0, pointerX: 0, pointerY: 0, view: window});
      }
      if (options) {
        $.extend(opts, options);
      }
      if (typeof window[ctor] === 'function') {
        event = new window[ctor](type, opts);
        element.dispatchEvent(event);
      }
      else if (document.createEvent) {
        event = document.createEvent(ctor);
        event.initEvent(type, opts.bubbles, opts.cancelable);
        element.dispatchEvent(event);
      }
      else if (typeof element.fireEvent === 'function') {
        event = $.extend(document.createEventObject(), opts);
        element.fireEvent('on' + type, event);
      }
      else if (typeof element[type]) {
        element[type]();
      }
    };
  
    /**
     * Provide jQuery UI like ability to get/set options for Bootstrap plugins.
     *
     * @param {string|object} key
     *   A string value of the option to set, can be dot like to a nested key.
     *   An object of key/value pairs.
     * @param {*} [value]
     *   (optional) A value to set for key.
     *
     * @returns {*}
     *   - Returns nothing if key is an object or both key and value parameters
     *   were provided to set an option.
     *   - Returns the a value for a specific setting if key was provided.
     *   - Returns an object of key/value pairs of all the options if no key or
     *   value parameter was provided.
     *
     * @see https://github.com/jquery/jquery-ui/blob/master/ui/widget.js
     *
     * @todo This isn't fully working since Bootstrap plugins don't allow
     * methods to return values.
     */
    Drupal.bootstrap.option = function (key, value) {
      var options = key;
      var parts, curOption, i;
  
      // Don't return a reference to the internal hash.
      if (arguments.length === 0) {
        return $.extend({}, this.options);
      }
  
      // Handle a specific option.
      if (typeof key === "string") {
        // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
        options = {};
        parts = key.split(".");
        key = parts.shift();
        if (parts.length) {
          curOption = options[key] = $.extend({}, this.options[key]);
          for (i = 0; i < parts.length - 1; i++) {
            curOption[parts[i]] = curOption[parts[i]] || {};
            curOption = curOption[parts[i]];
          }
          key = parts.pop();
          if (arguments.length === 1) {
            return curOption[key] === undefined ? null : curOption[key];
          }
          curOption[key] = value;
        }
        else {
          if (arguments.length === 1) {
            return this.options[key] === undefined ? null : this.options[key];
          }
          options[key] = value;
        }
      }
  
      // Set the new option(s).
      for (key in options) {
        if (!options.hasOwnProperty(key)) continue;
        this.options[key] = options[key];
      }
      return this;
    };
  
  })(window.jQuery, window.Drupal, window.drupalSettings);
  ;
  (function ($, _) {
  
    /**
     * Class to help modify attributes.
     *
     * @param {object} object
     *   An object to initialize attributes with.
     *
     * @constructor
     */
    var Attributes = function (object) {
      this.data = object && _.isObject(object) && _.clone(object) || {};
    };
  
    /**
     * Renders the attributes object as a string to inject into an HTML element.
     *
     * @returns {string}
     */
    Attributes.prototype.toString = function () {
      var output = '';
      var name, value;
      var checkPlain = function (str) {
        return str && str.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;') || '';
      };
      for (name in this.data) {
        if (!this.data.hasOwnProperty(name)) continue;
        value = this.data[name];
        if (_.isFunction(value)) value = value();
        if (_.isObject(value)) value = _.values(value);
        if (_.isArray(value)) value = value.join(' ');
        output += ' ' + checkPlain(name) + '="' + checkPlain(value) + '"';
      }
      return output;
    };
  
    /**
     * Add class(es) to the array.
     *
     * @param {string|Array} value
     *   An individual class or an array of classes to add.
     *
     * @return {Attributes}
     *
     * @chainable
     */
    Attributes.prototype.addClass = function (value) {
      var classes = this.getClasses();
      value = [].concat(classes, value);
      this.set('class', _.uniq(value));
      return this;
    };
  
    /**
     * Returns whether the requested attribute exists.
     *
     * @param {string} name
     *   An attribute name to check.
     *
     * @return {boolean}
     *   TRUE or FALSE
     */
    Attributes.prototype.exists = function (name) {
      return this.data[name] !== void(0) && this.data[name] !== null;
    };
  
    /**
     * Retrieve a specific attribute from the array.
     *
     * @param {string} name
     *   The specific attribute to retrieve.
     * @param {*} defaultValue
     *   (optional) The default value to set if the attribute does not exist.
     *
     * @return {*}
     *   A specific attribute value, passed by reference.
     */
    Attributes.prototype.get = function (name, defaultValue) {
      if (!this.exists(name)) this.data[name] = defaultValue;
      return this.data[name];
    };
  
    /**
     * Retrieves a cloned copy of the internal attributes data object.
     *
     * @returns {Object}
     */
    Attributes.prototype.getData = function () {
      return _.clone(this.data);
    };
  
    /**
     * Retrieves classes from the array.
     *
     * @return {Array}
     *   The classes array.
     */
    Attributes.prototype.getClasses = function () {
      var classes = [].concat(this.get('class', []));
      return _.uniq(classes);
    };
  
    /**
     * Indicates whether a class is present in the array.
     *
     * @param {string|Array} name
     *   The class(es) to search for.
     *
     * @return {boolean}
     *   TRUE or FALSE
     */
    Attributes.prototype.hasClass = function (name) {
      name = [].concat(name);
      var classes = this.getClasses();
      var found = false;
      _.each(name, function (value) { if (_.indexOf(classes, value) !== -1) found = true; });
      return found;
    };
  
    /**
     * Merges multiple values into the array.
     *
     * @param {object} values
     *   An associative key/value array.
     * @param {boolean} [recursive]
     *   Flag determining whether or not to recursively merge key/value pairs.
     *
     * @return {Attributes}
     *
     * @chainable
     */
    Attributes.prototype.merge = function (values, recursive) {
      values = values instanceof Attributes ? values.getData() : values;
      if (recursive === void(0) || recursive) {
        this.data = $.extend(true, {}, this.data, values);
      }
      else {
        $.extend(this.data, values);
      }
      return this;
    };
  
    /**
     * Removes an attribute from the array.
     *
     * @param {string} name
     *   The name of the attribute to remove.
     *
     * @return {Attributes}
     *
     * @chainable
     */
    Attributes.prototype.remove = function (name) {
      if (this.exists(name)) delete this.data[name];
      return this;
    };
  
    /**
     * Removes a class from the attributes array.
     *
     * @param {string|Array} value
     *   An individual class or an array of classes to remove.
     *
     * @return {Attributes}
     *
     * @chainable
     */
    Attributes.prototype.removeClass = function (value) {
      this.set('class', _.without(this.getClasses(), [].concat(value)));
      return this;
    };
  
    /**
     * Replaces a class in the attributes array.
     *
     * @param {string} oldValue
     *   The old class to remove.
     * @param {string} newValue
     *   The new class. It will not be added if the old class does not exist.
     *
     * @return {Attributes}
     *
     * @chainable
     */
    Attributes.prototype.replaceClass = function (oldValue, newValue) {
      var classes = this.getClasses();
      var i = _.indexOf(oldValue, classes);
      if (i >= 0) {
        classes[i] = newValue;
        this.set('class', classes);
      }
      return this;
    };
  
    /**
     * Sets an attribute on the array.
     *
     * @param {string} name
     *   The name of the attribute to set.
     * @param {*} value
     *   The value of the attribute to set.
     *
     * @return {Attributes}
     *
     * @chainable
     */
    Attributes.prototype.set = function (name, value) {
      this.data[name] = value;
      return this;
    };
  
    /**
     * Creates an Attributes instance.
     *
     * @param {object|Attributes} object
     *   An object to initialize attributes with.
     *
     * @returns {Attributes}
     *
     * @global
     *
     * @constructor
     */
    window.Attributes = function (object) {
      return object instanceof Attributes ? object : new Attributes(object);
    };
  
  })(window.jQuery, window._);
  ;
  /**
   * @file
   * Theme hooks for the Drupal Bootstrap base theme.
   */
  (function ($, Drupal, Bootstrap) {
  
    /**
     * Fallback for theming an icon if the Icon API module is not installed.
     */
    if (!Drupal.icon) Drupal.icon = { bundles: {} };
    if (!Drupal.theme.icon || Drupal.theme.prototype.icon) {
      $.extend(Drupal.theme, /** @lends Drupal.theme */ {
        /**
         * Renders an icon.
         *
         * @param {string} bundle
         *   The bundle which the icon belongs to.
         * @param {string} icon
         *   The name of the icon to render.
         * @param {object|Attributes} [attributes]
         *   An object of attributes to also apply to the icon.
         *
         * @returns {string}
         */
        icon: function (bundle, icon, attributes) {
          if (!Drupal.icon.bundles[bundle]) return '';
          attributes = Attributes(attributes).addClass('icon').set('aria-hidden', 'true');
          icon = Drupal.icon.bundles[bundle](icon, attributes);
          return '<span' + attributes + '></span>';
        }
      });
    }
  
    /**
     * Callback for modifying an icon in the "bootstrap" icon bundle.
     *
     * @param {string} icon
     *   The icon being rendered.
     * @param {Attributes} attributes
     *   Attributes object for the icon.
     */
    Drupal.icon.bundles.bootstrap = function (icon, attributes) {
      attributes.addClass(['glyphicon', 'glyphicon-' + icon]);
    };
  
    /**
     * Add necessary theming hooks.
     */
    $.extend(Drupal.theme, /** @lends Drupal.theme */ {
  
      /**
       * Renders a Bootstrap AJAX glyphicon throbber.
       *
       * @returns {string}
       */
      ajaxThrobber: function () {
        return Drupal.theme.bootstrapIcon('refresh', {'class': ['ajax-throbber', 'glyphicon-spin'] });
      },
  
      /**
       * Renders a button element.
       *
       * @param {object|Attributes} attributes
       *   An object of attributes to apply to the button. If it contains one of:
       *   - value: The label of the button.
       *   - context: The context type of Bootstrap button, can be one of:
       *     - default
       *     - primary
       *     - success
       *     - info
       *     - warning
       *     - danger
       *     - link
       *
       * @returns {string}
       */
      button: function (attributes) {
        attributes = Attributes(attributes).addClass('btn');
        var context = attributes.get('context', 'default');
        var label = attributes.get('value', '');
        attributes.remove('context').remove('value');
        if (!attributes.hasClass(['btn-default', 'btn-primary', 'btn-success', 'btn-info', 'btn-warning', 'btn-danger', 'btn-link'])) {
          attributes.addClass('btn-' + Bootstrap.checkPlain(context));
        }
        return '<button' + attributes + '>' + label + '</button>';
      },
  
      /**
       * Alias for "button" theme hook.
       *
       * @param {object|Attributes} attributes
       *   An object of attributes to apply to the button.
       *
       * @see Drupal.theme.button()
       *
       * @returns {string}
       */
      btn: function (attributes) {
        return Drupal.theme('button', attributes);
      },
  
      /**
       * Renders a button block element.
       *
       * @param {object|Attributes} attributes
       *   An object of attributes to apply to the button.
       *
       * @see Drupal.theme.button()
       *
       * @returns {string}
       */
      'btn-block': function (attributes) {
        return Drupal.theme('button', Attributes(attributes).addClass('btn-block'));
      },
  
      /**
       * Renders a large button element.
       *
       * @param {object|Attributes} attributes
       *   An object of attributes to apply to the button.
       *
       * @see Drupal.theme.button()
       *
       * @returns {string}
       */
      'btn-lg': function (attributes) {
        return Drupal.theme('button', Attributes(attributes).addClass('btn-lg'));
      },
  
      /**
       * Renders a small button element.
       *
       * @param {object|Attributes} attributes
       *   An object of attributes to apply to the button.
       *
       * @see Drupal.theme.button()
       *
       * @returns {string}
       */
      'btn-sm': function (attributes) {
        return Drupal.theme('button', Attributes(attributes).addClass('btn-sm'));
      },
  
      /**
       * Renders an extra small button element.
       *
       * @param {object|Attributes} attributes
       *   An object of attributes to apply to the button.
       *
       * @see Drupal.theme.button()
       *
       * @returns {string}
       */
      'btn-xs': function (attributes) {
        return Drupal.theme('button', Attributes(attributes).addClass('btn-xs'));
      },
  
      /**
       * Renders a glyphicon.
       *
       * @param {string} name
       *   The name of the glyphicon.
       * @param {object|Attributes} [attributes]
       *   An object of attributes to apply to the icon.
       *
       * @returns {string}
       */
      bootstrapIcon: function (name, attributes) {
        return Drupal.theme('icon', 'bootstrap', name, attributes);
      }
  
    });
  
  })(window.jQuery, window.Drupal, window.Drupal.bootstrap);
  ;
  /**
   * @file
   * Bootstrap Popovers.
   */
  
  var Drupal = Drupal || {};
  
  (function ($, Drupal, Bootstrap) {
    "use strict";
  
    /**
     * Extend the Bootstrap Popover plugin constructor class.
     */
    Bootstrap.extendPlugin('popover', function (settings) {
      return {
        DEFAULTS: {
          animation: !!settings.popover_animation,
          html: !!settings.popover_html,
          placement: settings.popover_placement,
          selector: settings.popover_selector,
          trigger: settings.popover_trigger,
          triggerAutoclose: !!settings.popover_trigger_autoclose,
          title: settings.popover_title,
          content: settings.popover_content,
          delay: parseInt(settings.popover_delay, 10),
          container: settings.popover_container
        }
      };
    });
  
    /**
     * Bootstrap Popovers.
     *
     * @todo This should really be properly delegated if selector option is set.
     */
    Drupal.behaviors.bootstrapPopovers = {
      attach: function (context) {
  
        // Popover autoclose.
        if ($.fn.popover.Constructor.DEFAULTS.triggerAutoclose) {
          var $currentPopover = null;
          $(document)
            .on('show.bs.popover', '[data-toggle=popover]', function () {
              var $trigger = $(this);
              var popover = $trigger.data('bs.popover');
  
              // Only keep track of clicked triggers that we're manually handling.
              if (popover.options.originalTrigger === 'click') {
                if ($currentPopover && !$currentPopover.is($trigger)) {
                  $currentPopover.popover('hide');
                }
                $currentPopover = $trigger;
              }
            })
            .on('click', function (e) {
              var $target = $(e.target);
              var popover = $target.is('[data-toggle=popover]') && $target.data('bs.popover');
              if ($currentPopover && !$target.is('[data-toggle=popover]') && !$target.closest('.popover.in')[0]) {
                $currentPopover.popover('hide');
                $currentPopover = null;
              }
            })
          ;
        }
  
        var elements = $(context).find('[data-toggle=popover]').toArray();
        for (var i = 0; i < elements.length; i++) {
          var $element = $(elements[i]);
          var options = $.extend({}, $.fn.popover.Constructor.DEFAULTS, $element.data());
  
          // Store the original trigger.
          options.originalTrigger = options.trigger;
  
          // If the trigger is "click", then we'll handle it manually here.
          if (options.trigger === 'click') {
            options.trigger = 'manual';
          }
  
          // Retrieve content from a target element.
          var $target = $(options.target || $element.is('a[href^="#"]') && $element.attr('href')).clone();
          if (!options.content && $target[0]) {
            $target.removeClass('visually-hidden hidden').removeAttr('aria-hidden');
            options.content = $target.wrap('<div/>').parent()[options.html ? 'html' : 'text']() || '';
          }
  
          // Initialize the popover.
          $element.popover(options);
  
          // Handle clicks manually.
          if (options.originalTrigger === 'click') {
            // To ensure the element is bound multiple times, remove any
            // previously set event handler before adding another one.
            $element
              .off('click.drupal.bootstrap.popover')
              .on('click.drupal.bootstrap.popover', function (e) {
                $(this).popover('toggle');
                e.preventDefault();
                e.stopPropagation();
              })
            ;
          }
        }
      },
      detach: function (context) {
        // Destroy all popovers.
        $(context).find('[data-toggle="popover"]')
          .off('click.drupal.bootstrap.popover')
          .popover('destroy')
        ;
      }
    };
  
  })(window.jQuery, window.Drupal, window.Drupal.bootstrap);
  ;
  /**
   * @file
   * Bootstrap Tooltips.
   */
  
  var Drupal = Drupal || {};
  
  (function ($, Drupal, Bootstrap) {
    "use strict";
  
    /**
     * Extend the Bootstrap Tooltip plugin constructor class.
     */
    Bootstrap.extendPlugin('tooltip', function (settings) {
      return {
        DEFAULTS: {
          animation: !!settings.tooltip_animation,
          html: !!settings.tooltip_html,
          placement: settings.tooltip_placement,
          selector: settings.tooltip_selector,
          trigger: settings.tooltip_trigger,
          delay: parseInt(settings.tooltip_delay, 10),
          container: settings.tooltip_container
        }
      };
    });
  
    /**
     * Bootstrap Tooltips.
     *
     * @todo This should really be properly delegated if selector option is set.
     */
    Drupal.behaviors.bootstrapTooltips = {
      attach: function (context) {
        var elements = $(context).find('[data-toggle="tooltip"]').toArray();
        for (var i = 0; i < elements.length; i++) {
          var $element = $(elements[i]);
          var options = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, $element.data());
          $element.tooltip(options);
        }
      },
      detach: function (context) {
        // Destroy all tooltips.
        $(context).find('[data-toggle="tooltip"]').tooltip('destroy');
      }
    };
  
  })(window.jQuery, window.Drupal, window.Drupal.bootstrap);
  ;
  /**
   * @file
   * Javascript for the Geojson Google Maps.
   *
   * @source: https://github.com/JasonSanford/GeoJSON-to-Google-Maps
   */
  
  (function ($, Drupal) {
  
    'use strict';
  
    Drupal.googleGeoJson = function (geojson, options) {
  
      var _geometryToGoogleMaps = function (geojsonGeometry, opts, geojsonProperties) {
  
        var googleObj;
        var bounds;
        var paths = [];
        var path = [];
        var i;
        var j;
        var k;
        var coord;
        var ll;
  
        switch (geojsonGeometry.type) {
          case 'Point':
            opts.position = new google.maps.LatLng(geojsonGeometry.coordinates[1], geojsonGeometry.coordinates[0]);
            bounds = new google.maps.LatLngBounds();
            bounds.extend(opts.position);
            googleObj = new google.maps.Marker(opts);
            googleObj.set('bounds', bounds);
            if (geojsonProperties) {
              googleObj.set('geojsonProperties', geojsonProperties);
            }
            break;
  
          case 'MultiPoint':
            googleObj = [];
            bounds = new google.maps.LatLngBounds();
            for (i = 0; i < geojsonGeometry.coordinates.length; i++) {
              opts.position = new google.maps.LatLng(geojsonGeometry.coordinates[i][1], geojsonGeometry.coordinates[i][0]);
              bounds.extend(opts.position);
              googleObj.push(new google.maps.Marker(opts));
            }
            if (geojsonProperties) {
              for (k = 0; k < googleObj.length; k++) {
                googleObj[k].set('geojsonProperties', geojsonProperties);
              }
            }
            for (k = 0; k < googleObj.length; k++) {
              googleObj[k].set('bounds', bounds);
            }
            break;
  
          case 'LineString':
            bounds = new google.maps.LatLngBounds();
            path = [];
            for (i = 0; i < geojsonGeometry.coordinates.length; i++) {
              coord = geojsonGeometry.coordinates[i];
              ll = new google.maps.LatLng(coord[1], coord[0]);
              bounds.extend(ll);
              path.push(ll);
            }
            opts.path = path;
            googleObj = new google.maps.Polyline(opts);
            googleObj.set('bounds', bounds);
            if (geojsonProperties) {
              googleObj.set('geojsonProperties', geojsonProperties);
            }
            break;
  
          case 'MultiLineString':
            googleObj = [];
            bounds = new google.maps.LatLngBounds();
            for (i = 0; i < geojsonGeometry.coordinates.length; i++) {
              path = [];
              for (j = 0; j < geojsonGeometry.coordinates[i].length; j++) {
                coord = geojsonGeometry.coordinates[i][j];
                ll = new google.maps.LatLng(coord[1], coord[0]);
                bounds.extend(ll);
                path.push(ll);
              }
              opts.path = path;
              googleObj.push(new google.maps.Polyline(opts));
            }
            if (geojsonProperties) {
              for (k = 0; k < googleObj.length; k++) {
                googleObj[k].set('geojsonProperties', geojsonProperties);
              }
            }
            for (k = 0; k < googleObj.length; k++) {
              googleObj[k].set('bounds', bounds);
            }
            break;
  
          case 'Polygon':
            paths = [];
            bounds = new google.maps.LatLngBounds();
            for (i = 0; i < geojsonGeometry.coordinates.length; i++) {
              path = [];
              for (j = 0; j < geojsonGeometry.coordinates[i].length; j++) {
                ll = new google.maps.LatLng(geojsonGeometry.coordinates[i][j][1], geojsonGeometry.coordinates[i][j][0]);
                bounds.extend(ll);
                path.push(ll);
              }
              paths.push(path);
            }
            opts.paths = paths;
            googleObj = new google.maps.Polygon(opts);
            googleObj.set('bounds', bounds);
            if (geojsonProperties) {
              googleObj.set('geojsonProperties', geojsonProperties);
            }
            break;
  
          case 'MultiPolygon':
            googleObj = [];
            bounds = new google.maps.LatLngBounds();
            for (i = 0; i < geojsonGeometry.coordinates.length; i++) {
              paths = [];
              for (j = 0; j < geojsonGeometry.coordinates[i].length; j++) {
                path = [];
                for (k = 0; k < geojsonGeometry.coordinates[i][j].length; k++) {
                  ll = new google.maps.LatLng(geojsonGeometry.coordinates[i][j][k][1], geojsonGeometry.coordinates[i][j][k][0]);
                  bounds.extend(ll);
                  path.push(ll);
                }
                paths.push(path);
              }
              opts.paths = paths;
              googleObj.push(new google.maps.Polygon(opts));
            }
            if (geojsonProperties) {
              for (k = 0; k < googleObj.length; k++) {
                googleObj[k].set('geojsonProperties', geojsonProperties);
              }
            }
            for (k = 0; k < googleObj.length; k++) {
              googleObj[k].set('bounds', bounds);
            }
            break;
  
          case 'GeometryCollection':
            googleObj = [];
            if (!geojsonGeometry.geometries) {
              googleObj = _error('Invalid GeoJSON object: GeometryCollection object missing \'geometries\' member.');
            }
            else {
              for (i = 0; i < geojsonGeometry.geometries.length; i++) {
                googleObj.push(_geometryToGoogleMaps(geojsonGeometry.geometries[i], opts, geojsonProperties || null));
              }
            }
            break;
  
          default:
            googleObj = _error('Invalid GeoJSON object: Geometry object must be one of \'Point\', \'LineString\', \'Polygon\' or \'MultiPolygon\'.');
        }
  
        return googleObj;
  
      };
  
      var _error = function (message) {
  
        return {
          type: 'Error',
          message: message
        };
  
      };
  
      var obj;
  
      var opts = options || {};
  
      switch (geojson.type) {
        case 'FeatureCollection':
          if (!geojson.features) {
            obj = _error('Invalid GeoJSON object: FeatureCollection object missing \'features\' member.');
          }
          else {
            obj = [];
            for (var y = 0; y < geojson.features.length; y++) {
              obj.push(_geometryToGoogleMaps(geojson.features[y].geometry, opts, geojson.features[y].properties));
            }
          }
          break;
  
        case 'GeometryCollection':
          if (!geojson.geometries) {
            obj = _error('Invalid GeoJSON object: GeometryCollection object missing \'geometries\' member.');
          }
          else {
            obj = [];
            for (var z = 0; z < geojson.geometries.length; z++) {
              obj.push(_geometryToGoogleMaps(geojson.geometries[z], opts, geojson.geometries[z].properties));
            }
          }
          break;
  
        case 'Feature':
          if (!(geojson.properties && geojson.geometry)) {
            obj = _error('Invalid GeoJSON object: Feature object missing \'properties\' or \'geometry\' member.');
          }
          else {
            obj = _geometryToGoogleMaps(geojson.geometry, opts, geojson.properties);
          }
          break;
  
        case 'Point': case 'MultiPoint': case 'LineString': case 'MultiLineString': case 'Polygon': case 'MultiPolygon':
          obj = geojson.coordinates
            ? obj = _geometryToGoogleMaps(geojson, opts, geojson.properties)
            : _error('Invalid GeoJSON object: Geometry object missing \'coordinates\' member.');
          break;
  
        default:
          obj = _error('Invalid GeoJSON object: GeoJSON object must be one of \'Point\', \'LineString\', \'Polygon\', \'MultiPolygon\', \'Feature\', \'FeatureCollection\' or \'GeometryCollection\'.');
  
      }
  
      return obj;
    };
  
  })(jQuery, Drupal);
  ;
  /**
   * @file
   * Javascript for the Geofield Google Map formatter.
   */
  
  (function ($, Drupal, drupalSettings) {
  
    'use strict';
  
    /**
     * The Geofield Google Map formatter behavior.
     *
     * @type {Drupal~behavior}
     *
     * @prop {Drupal~behaviorAttach} attach
     *   Attaches the Geofield Google Map formatter behavior.
     */
    Drupal.behaviors.geofieldGoogleMap = {
      attach: function (context, settings) {
  
        if (drupalSettings['geofield_google_map']) {
          $(context).find('.geofield-google-map').once('geofield-processed').each(function (index, element) {
            var mapid = $(element).attr('id');
  
            // Check if the Map container really exists and hasn't been yet
            // initialized.
            if (drupalSettings['geofield_google_map'][mapid] && !Drupal.geoFieldMap.map_data[mapid]) {
  
              var map_settings = drupalSettings['geofield_google_map'][mapid]['map_settings'];
              var data = drupalSettings['geofield_google_map'][mapid]['data'];
  
              // Set the map_data[mapid] settings.
              Drupal.geoFieldMap.map_data[mapid] = map_settings;
  
              // Load before the Gmap Library, if needed.
              Drupal.geoFieldMap.loadGoogle(mapid, map_settings.gmap_api_key, map_settings.map_additional_libraries, function () {
                Drupal.geoFieldMap.map_initialize(mapid, map_settings, data, context);
              });
            }
          });
        }
      }
    };
  
    Drupal.geoFieldMap = {
  
      map_start: {
        center: {lat: 41.85, lng: -87.65},
        zoom: 18
      },
  
      map_data: {},
  
      // Google Maps are loaded lazily. In some situations load_google() is
      // called twice, which results in "You have included the Google Maps API
      // multiple times on this page. This may cause unexpected errors." errors.
      // This flag will prevent repeat $.getScript() calls.
      maps_api_loading: false,
  
      /**
       * Returns the re-coded google maps api language parameter, from html lang attribute.
       *
       * @param {string} html_language - The language id string
       *
       * @return {string} - The transformed language id string
       */
      googleMapsLanguage: function (html_language) {
        switch (html_language) {
          case 'zh-hans':
            html_language = 'zh-CN';
            break;
  
          case 'zh-hant':
            html_language = 'zh-TW';
            break;
        }
        return html_language;
      },
  
      /**
       * Provides the callback that is called when maps loads.
       */
      googleCallback: function () {
        var self = this;
        // Wait until the window load event to try to use the maps library.
        $(document).ready(function (e) {
          _.invoke(self.googleCallbacks, 'callback');
          self.googleCallbacks = [];
        });
      },
  
      /**
       * Adds a callback that will be called once the maps library is loaded.
       *
       * @param {string} callback - The callback
       */
      addCallback: function (callback) {
        var self = this;
        // Ensure callbacks array.
        self.googleCallbacks = self.googleCallbacks || [];
        self.googleCallbacks.push({callback: callback});
      },
  
      /**
       * Load Google Maps library.
       *
       * @param {string} mapid - The map id
       * @param {string} gmap_api_key - The gmap api key
       * @param {array} additional_libraries - Additional Libraries list
       * @param {string} callback - the Callback function  name
       */
      loadGoogle: function (mapid, gmap_api_key, additional_libraries, callback) {
        var self = this;
        var html_language = $('html').attr('lang') || 'en';
  
        // Add the callback.
        self.addCallback(callback);
  
        // Check for google maps.
        if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
          if (self.maps_api_loading === true) {
            return;
          }
  
          self.maps_api_loading = true;
          // Google maps isn't loaded so lazy load google maps.
          // Default script path.
          var scriptPath = self.map_data[mapid]['gmap_api_localization'] + '?v=3.exp&sensor=false&language=' + self.googleMapsLanguage(html_language);
  
          // If a Google API key is set, use it.
          if (gmap_api_key) {
            scriptPath += '&key=' + gmap_api_key;
          }
  
          if (additional_libraries) {
            var libraries = [];
            for (var library in additional_libraries) {
              if (additional_libraries.hasOwnProperty(library)) {
                libraries.push(library);
              }
            }
            scriptPath += '&libraries=' + libraries.join();
          }
  
          $.getScript(scriptPath)
            .done(function () {
              self.maps_api_loading = false;
              self.googleCallback();
            });
  
        }
        else {
          // Google maps loaded. Run callback.
          self.googleCallback();
        }
      },
  
      checkImage: function (imageSrc, setIcon, logError) {
        var img = new Image();
        img.src = imageSrc;
        img.onload = setIcon;
        img.onerror = logError;
      },
  
      place_feature: function (feature, mapid) {
        var self = this;
        var icon_image = null;
  
        // Override and set icon image with geojsonProperties.icon, if set as not
        // null/empty.
        if (feature.geojsonProperties.icon && feature.geojsonProperties.icon.length > 0) {
          icon_image = feature.geojsonProperties.icon;
        }
  
        // Define the OverlappingMarkerSpiderfier flag.
        var oms = self.map_data[mapid].oms ? self.map_data[mapid].oms : null;
  
        // Set the personalized Icon Image, if set.
        if (feature.setIcon && icon_image && icon_image.length > 0) {
          self.checkImage(icon_image,
            // Success loading image.
            function () {
              feature.setIcon(icon_image);
            });
        }
  
        var map = self.map_data[mapid].map;
  
        // Add a default Tooltip on the title geojsonProperty, if existing.
        if (feature.setTitle && feature.geojsonProperties.tooltip) {
          feature.setTitle(feature.geojsonProperties.tooltip);
        }
  
        // If the feature is a Point, make it a Marker and extend the Map bounds.
        if (feature.getPosition) {
          if (oms) {
            self.map_data[mapid].oms.addMarker(feature);
          }
          else {
            feature.setMap(map);
          }
  
          // Generate the markers object index based on entity id (and geofield
          // cardinality), and add the marker to the markers object.
          var entity_id = feature['geojsonProperties']['entity_id'];
          if (self.map_data[mapid].geofield_cardinality && self.map_data[mapid].geofield_cardinality !== 1) {
            var i = 0;
            while (self.map_data[mapid].markers[entity_id + '-' + i]) {
              i++;
            }
            self.map_data[mapid].markers[entity_id + '-' + i] = feature;
          }
          else {
            self.map_data[mapid].markers[entity_id] = feature;
          }
  
          self.map_data[mapid].map_bounds.extend(feature.getPosition());
  
          // Check for eventual simple or OverlappingMarkerSpiderfier click
          // Listener.
          var clickListener = oms ? 'spider_click' : 'click';
          google.maps.event.addListener(feature, clickListener, function () {
            self.infowindow_open(mapid, feature);
          });
        }
  
        // If the feature is a Polyline or a Polygon, add to the Map and extend
        // the Map bounds.
        if (feature.getPath) {
          var feature_options = feature.geojsonProperties.path_options ? JSON.parse(feature.geojsonProperties.path_options) : {};
          feature.setOptions(feature_options);
          feature.setMap(map);
          var path = feature.getPath();
          path.forEach(function (element) {
            self.map_data[mapid].map_bounds.extend(element);
          });
          google.maps.event.addListener(feature, 'click', function (event) {
            self.infowindow_open(mapid, feature, event.latLng);
          });
        }
  
      },
  
      // Closes and open the Map Infowindow at the input feature.
      infowindow_open: function (mapid, feature, anchor) {
        var self = this;
        var map = self.map_data[mapid].map;
        var properties = feature.get('geojsonProperties');
        if (feature.setTitle && properties && properties.title) {
          feature.setTitle(properties.title);
        }
        map.infowindow.close();
        if (properties.description) {
          map.infowindow.setContent(properties.description);
  
          // Note: if the feature is a Marker (and not a Polyline/Polygon) its
          // extensions will override the infowindow anchor position, in the map.
          // infowindow.open method.
          map.infowindow.setPosition(anchor);
          setTimeout(function () {
            map.infowindow.open(map, feature);
          }, 200);
        }
      },
  
      map_refresh: function (mapid) {
        var self = this;
        setTimeout(function () {
          google.maps.event.trigger(self.map_data[mapid].map, 'resize');
        }, 10);
      },
  
      // Init Geofield Google Map and its functions.
      map_initialize: function (mapid, map_settings, data, context) {
        var self = this;
        $.noConflict();
  
        // If google and google.maps have been defined.
        if (google && google.maps) {
          var styledMapType;
  
          var mapOptions = {
            center: map_settings.map_center ? new google.maps.LatLng(map_settings.map_center.lat, map_settings.map_center.lon) : new google.maps.LatLng(42, 12.5),
            zoom: map_settings.map_zoom_and_pan.zoom.initial ? parseInt(map_settings.map_zoom_and_pan.zoom.initial) : 8,
            minZoom: map_settings.map_zoom_and_pan.zoom.min ? parseInt(map_settings.map_zoom_and_pan.zoom.min) : 1,
            maxZoom: map_settings.map_zoom_and_pan.zoom.max ? parseInt(map_settings.map_zoom_and_pan.zoom.max) : 20,
            gestureHandling: map_settings.map_zoom_and_pan.gestureHandling || 'auto',
            mapTypeId: map_settings.map_controls.map_type_id || 'roadmap'
          };
  
          // Manage the old scrollwheel & draggable settings (deprecated by google maps api).
          if (!map_settings.map_zoom_and_pan.scrollwheel && !map_settings.map_zoom_and_pan.gestureHandling) {
            mapOptions.scrollwheel = false;
          }
          if (!map_settings.map_zoom_and_pan.draggable && !map_settings.map_zoom_and_pan.gestureHandling) {
            mapOptions.draggable = false;
          }
  
          if (map_settings.map_controls.disable_default_ui) {
            mapOptions.disableDefaultUI = map_settings.map_controls.disable_default_ui;
          }
          else {
            // Implement Custom Style Map, if Set.
            if (map_settings.custom_style_map && map_settings.custom_style_map.custom_style_control && map_settings.custom_style_map.custom_style_name.length > 0 && map_settings.custom_style_map.custom_style_options.length > 0) {
              var customMapStyleName = map_settings.custom_style_map.custom_style_name;
              var customMapStyle = JSON.parse(map_settings.custom_style_map.custom_style_options);
              styledMapType = new google.maps.StyledMapType(customMapStyle, {name: customMapStyleName});
              map_settings.map_controls.map_type_control_options_type_ids.push('custom_styled_map');
            }
  
            mapOptions.zoomControl = !!map_settings.map_controls.zoom_control;
            mapOptions.mapTypeControl = !!map_settings.map_controls.map_type_control;
            mapOptions.mapTypeControlOptions = {
              mapTypeIds: map_settings.map_controls.map_type_control_options_type_ids ? map_settings.map_controls.map_type_control_options_type_ids : ['roadmap', 'satellite', 'hybrid'],
              position: google.maps.ControlPosition.TOP_LEFT
            };
            mapOptions.scaleControl = !!map_settings.map_controls.scale_control;
            mapOptions.streetViewControl = !!map_settings.map_controls.street_view_control;
            mapOptions.fullscreenControl = !!map_settings.map_controls.fullscreen_control;
          }
  
          // Add map_additional_options if any.
          if (map_settings.map_additional_options.length > 0) {
            var additionalOptions = JSON.parse(map_settings.map_additional_options);
            // Transforms additionalOptions "true", "false" values into true &
            // false.
            for (var prop in additionalOptions) {
              if (additionalOptions.hasOwnProperty(prop)) {
                if (additionalOptions[prop] === 'true') {
                  additionalOptions[prop] = true;
                }
                if (additionalOptions[prop] === 'false') {
                  additionalOptions[prop] = false;
                }
              }
            }
            // Merge mapOptions with additionalOptions.
            $.extend(mapOptions, additionalOptions);
          }
  
          // Define the Geofield Google Map.
          var map = new google.maps.Map(document.getElementById(mapid), mapOptions);
  
          // Add the Map Reset Control, if set.
          if (map_settings.map_zoom_and_pan.map_reset) {
            var mapResetControlPosition = map_settings.map_zoom_and_pan.map_reset_position || 'TOP_RIGHT';
  
            // Create the DIV to hold the control and call the mapResetControl()
            // constructor passing in this DIV.
            var mapResetControlDiv = document.createElement('div');
            mapResetControlDiv.style.zIndex = "10";
            mapResetControlDiv.index = 1;
            new self.map_reset_control(mapResetControlDiv, mapid);
            map.controls[google.maps.ControlPosition[mapResetControlPosition]].push(mapResetControlDiv);
          }
  
          if (Drupal.geoFieldMap.map_geocoder_control && map_settings.map_geocoder.control) {
            var mapGeocoderControlPosition = map_settings.map_geocoder.settings.position || 'TOP_RIGHT';
            var mapGeocoderControlDiv = document.createElement('div');
            Drupal.geoFieldMap.map_data[mapid].geocoder_control = new Drupal.geoFieldMap.map_geocoder_control(mapGeocoderControlDiv, mapid);
            mapGeocoderControlDiv.index = 1;
            map.controls[google.maps.ControlPosition[mapGeocoderControlPosition]].push(Drupal.geoFieldMap.map_data[mapid].geocoder_control);
            Drupal.geoFieldMap.map_geocoder_control.autocomplete(mapid, map_settings.map_geocoder.settings, $(Drupal.geoFieldMap.map_data[mapid].geocoder_control), 'formatter', 'gmap');
          }
  
          // If defined a Custom Map Style, associate the styled map with
          // custom_styled_map MapTypeId and set it to display.
          if (styledMapType) {
            map.mapTypes.set('custom_styled_map', styledMapType);
            // Set Custom Map Style to Default, if requested.
            if (map_settings.custom_style_map && map_settings.custom_style_map.custom_style_default) {
              map.setMapTypeId('custom_styled_map');
            }
          }
  
          // Define a mapid self property, so other code can interact with it.
          self.map_data[mapid].map = map;
          self.map_data[mapid].map_options = mapOptions;
          self.map_data[mapid].features = data.features;
          self.map_data[mapid].markers = {};
  
          // Define the MapBounds property.
          self.map_data[mapid].map_bounds = new google.maps.LatLngBounds();
  
          // Set the zoom force and center property for the map.
          self.map_data[mapid].zoom_force = !!map_settings.map_zoom_and_pan.zoom.force;
          self.map_data[mapid].center_force = !!map_settings.map_center.center_force;
  
          // Parse the Geojson data into Google Maps Locations.
          var features = data.features && data.features.length > 0 ? Drupal.googleGeoJson(data) : null;
  
          if (features && features.length > 0 && (!features.type || features.type !== 'Error')) {
  
            /**
             * Implement  OverlappingMarkerSpiderfier if its control set true.
             */
            if (map_settings.map_oms && map_settings.map_oms.map_oms_control && OverlappingMarkerSpiderfier) {
              var omsOptions = map_settings.map_oms.map_oms_options.length > 0 ? JSON.parse(map_settings.map_oms.map_oms_options) : {
                markersWontMove: true,
                markersWontHide: true,
                basicFormatEvents: true,
                keepSpiderfied: true
              };
              self.map_data[mapid].oms = new OverlappingMarkerSpiderfier(map, omsOptions);
            }
  
            map.infowindow = new google.maps.InfoWindow({
              content: ''
            });
  
            // If the map.infowindow is defined, add an event listener for the
            // Ajax Infowindow Popup.
            google.maps.event.addListener(map.infowindow, 'domready', function () {
              var element = document.createElement('div');
              element.innerHTML = map.infowindow.getContent().trim();
              var content = $('[data-geofield-google-map-ajax-popup]', element);
              if (content.length) {
                var url = content.data('geofield-google-map-ajax-popup');
                Drupal.ajax({url: url}).execute();
              }
              // Attach drupal behaviors on new content.
              $(element).each(function () {
                Drupal.attachBehaviors(this, drupalSettings);
              })
            });
  
            if (features.setMap) {
              self.place_feature(features, mapid);
            }
            else {
              for (var i in features) {
                if (features[i].setMap) {
                  self.place_feature(features[i], mapid);
                }
                else {
                  for (var j in features[i]) {
                    if (features[i][j].setMap) {
                      self.place_feature(features[i][j], mapid);
                    }
                  }
                }
              }
            }
  
            // Implement Markeclustering, if more than 1 marker on the map,
            // and the markercluster option is set to true.
            if (typeof MarkerClusterer !== 'undefined' && map_settings.map_markercluster.markercluster_control) {
  
              var markeclusterOption = {
                imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'
              };
  
              // Add markercluster_additional_options if any.
              if (map_settings.map_markercluster.markercluster_additional_options.length > 0) {
                var markeclusterAdditionalOptions = JSON.parse(map_settings.map_markercluster.markercluster_additional_options);
                // Merge markeclusterOption with markeclusterAdditionalOptions.
                $.extend(markeclusterOption, markeclusterAdditionalOptions);
              }
  
              // Define a markerCluster property, so other code can interact with
              // it.
              var markerCluster = [];
              var keys = Object.keys(self.map_data[mapid].markers);
              for (var k = 0; k < keys.length; k++) {
                markerCluster.push(self.map_data[mapid].markers[keys[k]]);
              }
              self.map_data[mapid].markerCluster = new MarkerClusterer(map, markerCluster, markeclusterOption);
            }
  
            // If the Map Initial State is defined by MapBounds,
            // and the map center is not forced.
            if (!self.mapBoundsAreNull(self.map_data[mapid].map_bounds) && !self.map_data[mapid].center_force) {
              map.fitBounds(self.map_data[mapid].map_bounds);
            }
            // Else if the Map Initial State is defined by Markers in the exact
            // same locations,.
            else if (self.map_data[mapid].markers.constructor === Object &&
              self.mapBoundsAreNull(self.map_data[mapid].map_bounds) &&
              // And the map center is not forced.
              !self.map_data[mapid].center_force) {
              map.setCenter(self.map_data[mapid].markers[Object.keys(self.map_data[mapid].markers)[0]].getPosition());
            }
  
          }
  
          google.maps.event.addListenerOnce(map, 'bounds_changed', function () {
            // Force the Map Zoom if requested.
            if (self.map_data[mapid].zoom_force) {
              self.map_data[mapid].map.setZoom(self.map_data[mapid].map_options.zoom);
            }
          });
  
          // At the beginning (once) ...
          google.maps.event.addListenerOnce(map, 'idle', function () {
  
            // Open the Feature infowindow, if so set.
            if (self.map_data[mapid].map_marker_and_infowindow.force_open && parseInt(self.map_data[mapid].map_marker_and_infowindow.force_open) === 1) {
              // map.setCenter(features[0].getPosition());
              self.infowindow_open(mapid, features[0]);
            }
  
            // In case of map initial position not forced, and zooFiner not
            // null/neutral, adapt the Map Zoom and the Start Zoom accordingly.
            if (!self.map_data[mapid].zoom_force && self.map_data[mapid].map_zoom_and_pan.zoom.finer && self.map_data[mapid].map_zoom_and_pan.zoom.finer !== 0) {
              map.setOptions({
                zoom: map.getZoom() + parseInt(self.map_data[mapid].map_zoom_and_pan.zoom.finer)
              });
            }
  
            // Update map initial state after everything is settled.
            self.map_set_start_state(mapid, map.getCenter(), map.getZoom());
  
            // Trigger a custom event on Geofield Map initialized, with mapid.
            $(context).trigger('geofieldMapInit', mapid);
          });
  
        }
      },
  
      mapBoundsAreNull: function (mapBounds) {
        var north_east = mapBounds.getNorthEast();
        var south_west = mapBounds.getSouthWest();
        return north_east.toString() === south_west.toString();
  
      },
  
      map_set_start_state: function (mapid, center, zoom) {
        var self = this;
        self.map_data[mapid].map_start_center = center;
        self.map_data[mapid].map_start_zoom = zoom;
      },
  
      map_reset_control: function (controlDiv, mapid) {
        // Set CSS for the control border.
        var controlUI = document.createElement('div');
        controlUI.style.backgroundColor = '#fff';
        controlUI.style.boxShadow = 'rgba(0,0,0,.3) 0px 1px 4px -1px';
        controlUI.style.cursor = 'pointer';
        controlUI.title = Drupal.t('Click to reset the map to its initial state');
        controlUI.style.margin = '10px';
        controlUI.style.position = 'relative';
        controlUI.id = 'geofield-map--' + mapid + '--reset-control';
        controlDiv.appendChild(controlUI);
  
        // Set CSS for the control interior.
        var controlText = document.createElement('div');
        controlText.style.position = 'relative';
        controlText.innerHTML = Drupal.t('Reset Map');
        controlText.style.padding = '0 17px';
        controlText.style.display = 'table-cell';
        controlText.style.height = '40px';
        controlText.style.fontSize = '18px';
        controlText.style.color = 'rgb(86,86,86)';
        controlText.style.textAlign = 'center';
        controlText.style.verticalAlign = 'middle';
        controlUI.appendChild(controlText);
  
        // Setup the click event listeners: simply set the map to Chicago.
        controlUI.addEventListener('click', function () {
          Drupal.geoFieldMap.map_data[mapid].map.setCenter(Drupal.geoFieldMap.map_data[mapid].map_start_center);
          Drupal.geoFieldMap.map_data[mapid].map.setZoom(Drupal.geoFieldMap.map_data[mapid].map_start_zoom);
        });
        return controlUI;
      }
  
    };
  
  })(jQuery, Drupal, drupalSettings);
  ;
  