text: require([
    'esri/views/MapView',
    'esri/Map',
    'esri/layers/FeatureLayer',
    'esri/widgets/Home',
    'esri/widgets/Legend',
    'esri/widgets/Expand',
    'esri/widgets/Fullscreen',
    'esri/widgets/Search',
    'esri/widgets/Search'
  
  ], function (MapView, Map, FeatureLayer, Home, Legend, Expand, Fullscreen, Search) {
    /**
     * @type {string}
     */
    let selectedSeason = null;
  
    /**
     * @type {LayerView}
     */
    let floodLayerView = null;
  
    /**
     * @type {Graphic[]}
     */
    let graphics = null;
  
    /**
     * @type {number}
     */
    const selectedclusteringUntilZoom = 11;
  
    /**
     * @type {ClusterConfig}
     */
    const clusterConfig = {
      type: 'cluster',
      popupTemplate: {
        content: [
          {
            type: 'text',
            text: 'This cluster represents <b>{cluster_count}</b> features.'
          },
          {
            type: 'text',
            text:
              'Predominant warning level in this cluster is <b>{cluster_type_current_level}</b>.'
          }
        ]
      }
    };
  
    /**
     * Returns an anchor with the feature sensor url for PopupTemplate.
     *
     * @param {Feature} feature - Feature
     * @return {string} -The anchor element in string format
     */
    function popupTemplateContentSensorAnchor (value) {
      if (value) {
        return '<a class="btn btn-bordered btn-cons btn-danger full-width m-b-20" ' +
          `href=${value} target="_blank">Analytics Dashboard</a>`;
      }
      return '';
    }
  
    /**
     * Returns a safe value of a feature attribute value for PopupTemplate.
     *
     * @param {Feature} feature - Feature
     * @return {string} -The table element in string format
     */
    function safeAttrValue (value) {
      return value || 'n/a';
    }
  
    /**
     * Returns a table with the feature attributes for PopupTemplate.
     *
     * @param {Feature} feature - Feature
     * @return {string} -The table element in string format
     */
    function popupTemplateContentAttrTable (attrs) {
      return '<table class="esri-widget__table"><tbody>' +
        `<tr><td>Warning Level</td><td>${safeAttrValue(attrs.current_level)}</td></tr>` +
        `<tr><td>DAM Area</td><td>${safeAttrValue(attrs.dam)}</td></tr>` +
        `<tr><td>Postcode</td><td>${safeAttrValue(attrs.postcode)}</td></tr>` +
        `<tr><td>Road</td><td>${safeAttrValue(attrs.road)}</td></tr>` +
        `<tr><td>Node Reference</td><td>${safeAttrValue(attrs.sd_number)}</td></tr>` +
        `<tr><td>Alert Level</td><td>${safeAttrValue(attrs.alert_level)}</td></tr>` +
        '</tbody></table>';
    }
  
    /**
     * Create and return the content of a feature for PopupTemplate.
     *
     * @param {Feature} feature - Feature
     * @return {HTMLDivElement} -The div element
     */
    function popupTemplateContent (feature) {
      const div = document.createElement('div');
      if (!feature || !feature.graphic || !feature.graphic.attributes) {
        return;
      }
      const attrs = feature.graphic.attributes;
      div.innerHTML = popupTemplateContentSensorAnchor(attrs.sensor_url) +
        popupTemplateContentAttrTable(attrs);
      return div;
    }
  
    /**
     * @type {PopupTemplate}
     */
    const popupTemplate = {
      title: '{current_level} - {postcode}',
      lastEditInfoEnabled: false,
      content: popupTemplateContent,
      fieldInfos: [
        {
          fieldName: 'sensor_url'
        },
        {
          fieldName: 'current_level'
        },
        {
          fieldName: 'dam'
        },
        {
          fieldName: 'postcode'
        },
        {
          fieldName: 'road'
        },
        {
          fieldName: 'sd_number'
        },
        {
          fieldName: 'alert_level'
        }
      ],
      outFields: [
        'current_level', 'dam', 'postcode', 'road', 'sd_number', 'alert_level', 'sensor_url'
      ]
    };
  
    /**
     * @type {FeatureLayer}
     */
    const layer = new FeatureLayer({
      url:
        'https://services8.arcgis.com/7LEpm0qhEOOXFxtS/arcgis/rest/services/sap_uu_dam_demo_sensors_wgs_master_view/FeatureServer/0',
      outFields: [
        'current_level', 'dam', 'postcode', 'road', 'sd_number', 'alert_level', 'sensor_url'
      ],
      featureReduction: clusterConfig,
      popupTemplate: popupTemplate
    });
  
    /**
     * @type {FeatureLayer}
     */
    const DAMlayer = new FeatureLayer({
      url:
        'https://services8.arcgis.com/7LEpm0qhEOOXFxtS/arcgis/rest/services/sus_uu_dam_area_wgs_master_view/FeatureServer/0'
    });
  
    /**
     * @type {FeatureLayer}
     */
    const IDASlayer = new FeatureLayer({
      url:
        'https://services8.arcgis.com/7LEpm0qhEOOXFxtS/arcgis/rest/services/sap_uu_all_idas_areas_wgs_master_view/FeatureServer/0'
    });
  
    /**
     * @type {Map}
     */
    const map = new Map({
      basemap: 'gray-vector',
      layers: [DAMlayer, IDASlayer, layer]
    });
  
    /**
     * @type {MapView}
     */
    const view = new MapView({
      map: map,
      container: 'viewDiv',
      center: [-2.66042, 54.001848],
      zoom: 6.5,
      popup: {
        dockEnabled: true,
        dockOptions: {
          buttonEnabled: false,
          breakpoint: false
        }
      }
    });
  
    // widgets
  
    view.ui.add(
      new Home({
        view: view
      }),
      'top-left'
    );
  
    view.ui.add(
      new Expand({
        expandTooltip: 'Show Legend',
        expanded: false,
        view: view,
        content: new Legend({ view: view })
      }),
      'top-left'
    );
  
    view.ui.add(
      new Expand({
        expandTooltip: 'Show Search',
        expanded: false,
        view: view,
        content: new Search({ view: view })
      }),
      'top-left'
    );
  
    view.ui.add(
      new Fullscreen({
        view: view,
        element: document.getElementById('applicationDiv')
      }),
      'top-right'
    );
  
    view.ui.add(
      new Expand({
        view: view,
        content: document.getElementById('infoDiv'),
        expandIconClass: 'esri-icon-filter',
        expanded: true
      }),
      'top-left'
    );
  
    // list
  
    /**
     * @type {HTMLUListElement}
     */
    const listNode = document.getElementById('listGraphics');
  
    /**
     * Compare two levels.
     *
     * @param {string} a - Level a
     * @param {string} b - Level b
     * @return {number} -1 if a < b, 0 if a = b, 1 if a > b
     */
    function compareLevels (a, b) {
      if (a === 'Low') {
        return -1;
      }
      if (b === 'Low') {
        return 1;
      }
      if (a === 'Medium') {
        return -1;
      }
      if (b === 'Medium') {
        return 1;
      }
      return 0;
    }
  
    /**
     * Compare two features.
     *
     * @param {Feature} a - Feature a
     * @param {Feature} b - Feature b
     * @return {number} -1 if a < b, 0 if a = b, 1 if a > b
     */
    function compareFeatures (a, b) {
      return -1 * compareLevels(
        a.attributes.current_level,
        b.attributes.current_level
      );
    }
  
    /**
     * Create and return a listNode item.
     *
     * @param {number} id - Data result id
     * @param {string} content - Content text
     * @param {string} level - Level {'High'|'Medium'|'Low'}
     * @return {HTMLLIElement} The <li> element
     */
    function listNodeCreateItem (id, content, level) {
      const li = document.createElement('li');
      li.classList.add('panel-result');
      li.tabIndex = 0;
      li.setAttribute('data-result-id', id);
      li.textContent = content;
      switch (level) {
        case 'High':
          li.classList.add('gradient-45deg-red-red');
          break;
        case 'Medium':
          li.classList.add('gradient-45deg-amber-amber');
          break;
        case 'Low':
          li.classList.add('gradient-45deg-green-teal');
          break;
        default:
          li.classList.add('gradient-45deg-light-blue-cyan');
          break;
      }
      return li;
    };
  
    /**
     * Update UI with the number of features by alert level in current map view.
     *
     * @param {{'Very High': number,'High': number,'Medium': number,'Low': number,}} level - Alert level
     */
    function updateLiveMapNumber (alertLevelCount) {
      document.getElementById('veryHighAlertMapNumberSpan')
        .innerText = alertLevelCount['Very High'];
      document.getElementById('highAlertMapNumberSpan')
        .innerText = alertLevelCount.High;
      document.getElementById('mediumAlertMapNumberSpan')
        .innerText = alertLevelCount.Medium;
      document.getElementById('lowAlertMapNumberSpan')
        .innerText = alertLevelCount.Low;
    }
  
    /**
     * Clear and build listNode.
     */
    function listNodeReset () {
      if (!graphics) {
        return;
      }
      const alertLevelCount = {
        'Very High': 0,
        High: 0,
        Medium: 0,
        Low: 0
      };
      const fragment = document.createDocumentFragment();
      graphics.sort(compareFeatures);
      graphics.forEach(function (result, index) {
        const attributes = result.attributes;
        if (
          !selectedSeason ||
          attributes.current_level === selectedSeason
        ) {
          if (Object.prototype.hasOwnProperty.call(alertLevelCount, attributes.current_level)) {
            alertLevelCount[attributes.current_level]++;
          }
          fragment.appendChild(
            listNodeCreateItem(
              index,
              attributes.current_level +
              ' | ' +
              attributes.postcode +
              ' | ' +
              attributes.road,
              attributes.current_level
            )
          );
        }
      });
      listNode.innerHTML = '';
      listNode.appendChild(fragment);
      updateLiveMapNumber(alertLevelCount);
    };
  
    view.whenLayerView(layer).then(function (layerView) {
      floodLayerView = layerView;
      layerView.watch('updating', function (value) {
        if (!value) {
          layerView
            .queryFeatures({
              geometry: view.extent,
              returnGeometry: true,
              orderByFields: ['current_level']
            })
            .then(function (results) {
              graphics = results.features;
              listNodeReset();
            })
            .catch(function (error) {
              console.error('query failed: ', error);
            });
        }
      });
    });
  
    /**
     * Click event handler for listNode.
     *
     * @param {MouseEvent} event
     */
    function listNodeClickHandler (event) {
      const target = event.target;
      const resultId = target.getAttribute('data-result-id');
      const result =
        resultId && graphics && graphics[parseInt(resultId, 10)];
      if (result) {
        view
          .goTo({
            center: [result.geometry.longitude, result.geometry.latitude],
            zoom: view.zoom + 4
          })
          .then(function () {
            view.popup.open({
              features: [result],
              location: result.geometry.centroid
            });
          })
          .catch(function (error) {
            if (error.current_level !== 'AbortError') {
              console.error(error);
            }
          });
      }
    };
  
    listNode.addEventListener('click', listNodeClickHandler);
  
    // seasons filter
  
    /**
     * Click event handler for seasonsElement.
     *
     * @param {MouseEvent} event
     */
    function seasonsElementClickHandler (event) {
      selectedSeason = event.currentTarget.getAttribute('data-season');
      floodLayerView.filter = {
        where: `current_level='${selectedSeason}'`
      };
      listNodeReset();
      event.stopPropagation();
    };
  
    document.querySelectorAll('div[data-season]').forEach(
      node => node.addEventListener('click', seasonsElementClickHandler)
    );
  
    // seasons reset fiter
  
    /**
     * Click event handler for seasonsReset.
     *
     * @param {MouseEvent} event
     */
    function seasonsResetClickHandler (event) {
      floodLayerView.filter = null;
      selectedSeason = null;
      listNodeReset();
    };
  
    document.getElementById('filterReset1')
      .addEventListener('click', seasonsResetClickHandler);
    document.getElementById('filterReset2')
      .addEventListener('click', seasonsResetClickHandler);
  
    // cluster
  
    /**
     * Zoom change handler for map view.
     *
     * @param {number} newValue
     */
    function viewZoomChangeHandler (newValue) {
      layer.featureReduction =
        newValue > selectedclusteringUntilZoom ? null : clusterConfig;
    }
  
    view.watch('zoom', viewZoomChangeHandler);
  });
  