<!DOCTYPE HTML>
<html>
  <head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
  <script src="https://sapui5.hana.ondemand.com/1.38.10/resources/sap-ui-core.js"
    id="sap-ui-bootstrap"
    data-sap-ui-libs="sap.ui.commons,sap.ui.core, sap.suite.ui.commons"
    data-sap-ui-theme="sap_bluecrystal"    
    data-sap-ui-xx-bindingSyntax="complex"
		data-sap-ui-resourceroots='{
				"demo": "./"
		}'>
  </script>
  <style>
    .sapSuiteGTHdrContent{
      height:1.5rem !important;
    }
    #oTileContent-content{
      min-height: 8rem;
    }
  </style>
  <!-- add sap.ui.table,sap.ui.ux3 and/or other libraries to 'data-sap-ui-libs' if required -->
  <script>
    var view = sap.ui.view({id:"idview1", viewName:"demo.CustomTile", type:sap.ui.core.mvc.ViewType.XML});
    view.placeAt("content");
  </script>
  </head>
  <body class="sapUiBody" role="application">
    <div id="content"></div>
  </body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<core:View
  xmlns="sap.suite.ui.commons"
  xmlns:mc="sap.suite.ui.microchart"
  xmlns:core="sap.ui.core"
  controllerName="demo.CustomTile">
  <GenericTile
    header="{/data/display_title_text}"
    subheader="{/data/display_subtitle_text}"
    size="Auto"
    frameType="OneByOne"
    press="onPress">
    <tileContent>
      <TileContent footer="{/data/display_info_text}" frameType="OneByOne">
        <content>
          <mc:ComparisonMicroChart
            size="M"
            id="compMicroChart">
            <mc:data>
              <mc:ComparisonMicroChartData
                title="{/data/comparisonModel/1/title}"
                value="{/data/comparisonModel/1/value}"
                color="{/data/comparisonModel/1/color}"/>
              <mc:ComparisonMicroChartData
                title="{/data/comparisonModel/2/title}"
                value="{/data/comparisonModel/2/value}"
                color="{/data/comparisonModel/2/color}"/>
              <mc:ComparisonMicroChartData
                title="{/data/comparisonModel/3/title}"
                value="{/data/comparisonModel/3/value}"
                color="{/data/comparisonModel/3/color}"/>
            </mc:data>
          </mc:ComparisonMicroChart>
        </content>
      </TileContent>
    </tileContent>
  </GenericTile>
</core:View>
(function() {
  "use strict";
  /*global jQuery, OData, sap, setTimeout, hasher */
  jQuery.sap.require("sap.ui.core.IconPool");
  jQuery.sap.require("sap.ushell.components.tiles.utils");

  sap.ui.controller("demo.CustomTile", {
    // handle to control/cancel browser's setTimeout()
    timer: null,
    // handle to control/cancel data.js OData.read()
    oDataRequest1: null,
    oDataRequest2: null,
    oDataRequest3: null,
    _setComparisonData: function(oModel, config) {

      var aData;

      //For testing purposes (data will be present when utilited within the Launchpad):
      var oDataProp = oModel.getProperty("/data");
      if (!oDataProp) {
        var aPropData = {
          "display_title_text": "Test Title",
          "display_subtitle_text": "Test Subtitle",
          "display_info_text": "Information"
        };
        oModel.setProperty("/data", aPropData);
      }

      if (config) {
        //For display when configuring the tile:
        aData = {
          1: {
            "title": "Example 1",
            "value": 25,
            "color": "Neutral"
          },
          2: {
            "title": "Example 2",
            "value": 15,
            "color": "Good"
          },
          3: {
            "title": "Example 3",
            "value": 35,
            "color": "Critical"
          }
        };
      } else {
        aData = {
          1: {
            "title": "...",
            "value": 0,
            "color": "Neutral"
          },
          2: {
            "title": "...",
            "value": 0,
            "color": "Neutral"
          },
          3: {
            "title": "...",
            "value": 0,
            "color": "Neutral"
          }
        };
      }

      oModel.setProperty("/data/comparisonModel", aData);
    },
    onInit: function() {
      var oView = this.getView();
      var oModel;
      var oViewData = oView.getViewData();

      //Adding to allow for running script outside of Launchpad:
      if (oViewData) {
        var oTileApi = oViewData.chip;
        var oConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, oTileApi.configurationUi.isEnabled(), false);
        var sKeywords;
        var aKeywords;
        var that = this;
        var sNavigationTargetUrl = oConfig.navigation_target_url;
        var sSystem;
        this.bIsDataRequested = false;
        sSystem = oTileApi.url.getApplicationSystem();
        if (sSystem) { // propagate system to target application
          sNavigationTargetUrl += ((sNavigationTargetUrl.indexOf("?") < 0) ? "?" : "&") + "sap-system=" + sSystem;
        }
        this.navigationTargetUrl = sNavigationTargetUrl;
        /*
         * Model of the applauncher tile consisting of
         *          config (tile configuration),
         *          data (dyanmic data read from a data source)
         *          nav (target URL set to '' in case of Admin UI), and
         *          search (highlight terms)
         */
        oModel = new sap.ui.model.json.JSONModel({
          config: oConfig,
          data: sap.ushell.components.tiles.utils.getDataToDisplay(oConfig, {
            number: (oTileApi.configurationUi.isEnabled() ? 1234 : "...")
          }),
          nav: {
            navigation_target_url: (oTileApi.configurationUi && oTileApi.configurationUi.isEnabled() ? "" : sNavigationTargetUrl)
          },
          search: {
            display_highlight_terms: []
          }
        });
        this._setComparisonData(oModel, oTileApi.configurationUi.isEnabled());
        oView.setModel(oModel);

        // implement search contract
        if (oTileApi.search) {
          // split and clean keyword string (may be comma + space delimited)
          sKeywords = oView.getModel().getProperty("/config/display_search_keywords");
          aKeywords = jQuery.grep(sKeywords.split(/[, ]+/), function(n, i) {
            return n && n !== "";
          });

          // add title and subtitle (if present) to keywords for better FLP searching
          if (oConfig.display_title_text && oConfig.display_title_text !== "" &&
            aKeywords.indexOf(oConfig.display_title_text) === -1) {
            aKeywords.push(oConfig.display_title_text);
          }
          if (oConfig.display_subtitle_text && oConfig.display_subtitle_text !== "" &&
            aKeywords.indexOf(oConfig.display_subtitle_text) === -1) {
            aKeywords.push(oConfig.display_subtitle_text);
          }
          if (oConfig.display_info_text && oConfig.display_info_text !== "" &&
            aKeywords.indexOf(oConfig.display_info_text) === -1) {
            aKeywords.push(oConfig.display_info_text);
          }

          // defined in search contract:
          oTileApi.search.setKeywords(aKeywords);
          oTileApi.search.attachHighlight(
            function(aHighlightWords) {
              // update model for highlighted search term
              oView.getModel().setProperty("/search/display_highlight_terms", aHighlightWords);
            }
          );
        }

        // implement bag update handler
        if (oTileApi.bag && oTileApi.bag.attachBagsUpdated) {
          // is only called by the FLP for bookmark tiles which have been updated via bookmark service
          oTileApi.bag.attachBagsUpdated(function(aUpdatedBagIds) {
            if (aUpdatedBagIds.indexOf("tileProperties") > -1) {
              sap.ushell.components.tiles.utils._updateTilePropertiesTexts(oView, oTileApi.bag.getBag('tileProperties'));
            }
          });
        }

        // implement preview contract
        if (oTileApi.preview) {
          oTileApi.preview.setTargetUrl(sNavigationTargetUrl);
          oTileApi.preview.setPreviewIcon(oConfig.display_icon_url);
          oTileApi.preview.setPreviewTitle(oConfig.display_title_text);
        }

        // implement refresh contract
        if (oTileApi.refresh) {
          oTileApi.refresh.attachRefresh(this.refreshHandler.bind(null, this));
        }

        // attach the refresh handler also for the visible contract, as we would like
        // on setting visible to true, to directly go and call the oData call
        if (oTileApi.visible) {
          oTileApi.visible.attachVisible(this.visibleHandler.bind(this));
        }

        // implement configurationUi contract: setup configuration UI
        if (oTileApi.configurationUi.isEnabled()) {
          oTileApi.configurationUi.setUiProvider(function() {
            // attach configuration UI provider, which is essentially a components.tiles.dynamicapplauncher.Configuration
            var oConfigurationUi = sap.ushell.components.tiles.utils.getConfigurationUi(oView, "view.Configuration");
            oTileApi.configurationUi.attachCancel(that.onCancelConfiguration.bind(null, oConfigurationUi));
            oTileApi.configurationUi.attachSave(that.onSaveConfiguration.bind(null, oConfigurationUi));
            return oConfigurationUi;
          });

          this.getView().getContent()[0].setTooltip(
            sap.ushell.components.tiles.utils.getResourceBundleModel().getResourceBundle()
            .getText("edit_configuration.tooltip")
          );
        } else {
          if (!oTileApi.preview || !oTileApi.preview.isEnabled()) {
            if (!sSystem) {
              sap.ushell.Container.addRemoteSystemForServiceUrl(oConfig.service_url1);
            } // else registration is skipped because registration has been done already
            // outside this controller (e.g. remote catalog registration)

            // start fetching data from backend service if not in preview or admin mode
            this.onUpdateDynamicData();
          }
        }

        // attach the tile actions provider for the actions contract
        if (oTileApi.actions) {
          //TODO check new property name with designer dudes
          var aActions = oConfig.actions,
            aExtendedActions;
          if (aActions) {
            aExtendedActions = aActions.slice();
          } else {
            aExtendedActions = [];
          }

          var tileSettingsAction = sap.ushell.components.tiles.utils.getTileSettingsAction(oModel, this.onSaveRuntimeSettings.bind(this));
          aExtendedActions.push(tileSettingsAction);

          oTileApi.actions.setActionsProvider(function() {
            return aExtendedActions;
          });
        }

      } else {
        //Design/Testing mode:
        oModel = new sap.ui.model.json.JSONModel();
        this._setComparisonData(oModel, true);
        oView.setModel(oModel);
      }

    },
    // convenience function to stop browser's timeout and OData calls
    stopRequests: function() {
      if (this.timer) {
        clearTimeout(this.timer);
      }
      if (this.oDataRequest) {
        try {
          this.oDataRequest.abort();
        } catch (e) {
          jQuery.sap.log.warning(e.name, e.message);
        }
      }
    },
    // destroy handler stops requests
    onExit: function() {
      this.stopRequests();
    },
    // trigger to show the configuration UI if the tile is pressed in Admin mode
    onPress: function() {
      var oView = this.getView(),
        oViewData = oView.getViewData(),
        oModel = oView.getModel(),
        sTargetUrl = oModel.getProperty("/nav/navigation_target_url"),
        oTileApi = oViewData.chip;
      if (oTileApi.configurationUi.isEnabled()) {
        oTileApi.configurationUi.display();
      } else if (sTargetUrl) {
        if (sTargetUrl[0] === '#') {
          hasher.setHash(sTargetUrl);
        } else {
          window.open(sTargetUrl, '_blank');
        }
      }
    },
    // dynamic data updater
    onUpdateDynamicData: function() {
      var oView = this.getView(),
        oConfig = oView.getModel().getProperty("/config"),
        nservice_refresh_interval = oConfig.service_refresh_interval;
      if (!nservice_refresh_interval) {
        nservice_refresh_interval = 0;
      } else if (nservice_refresh_interval < 10) {
        // log in English only
        jQuery.sap.log.warning(
          "Refresh Interval " + nservice_refresh_interval + " seconds for service URL " + oConfig.service_url1 + " is less than 10 seconds, which is not supported. " + "Increased to 10 seconds automatically.",
          null,
          "view.CustomTile.controller"
        );
        nservice_refresh_interval = 10;
      }
      if (oConfig.service_url1) {
        this.loadData(nservice_refresh_interval);
      }
    },
    extractData: function(oData) {
      var name,
        aKeys = ["results", "icon", "title", "number", "numberUnit", "info", "infoState", "infoStatus", "targetParams", "subtitle", "stateArrow", "numberState", "numberDigits", "numberFactor"];

      if (typeof oData === "object" && Object.keys(oData).length === 1) {
        name = Object.keys(oData)[0];
        if (jQuery.inArray(name, aKeys) === -1) {
          return oData[name];
        }
      }
      return oData;
    },
    // tile settings action UI save handler
    onSaveRuntimeSettings: function(oSettingsView) {
      var
        oViewModel = oSettingsView.getModel(),
        oTileApi = this.getView().getViewData().chip,
        oConfigToSave = this.getView().getModel().getProperty("/config");

      oConfigToSave.display_title_text = oViewModel.getProperty('/title');
      oConfigToSave.display_subtitle_text = oViewModel.getProperty('/subtitle');
      oConfigToSave.display_info_text = oViewModel.getProperty('/info');
      oConfigToSave.display_search_keywords = oViewModel.getProperty('/keywords');

      // use bag contract in order to store translatable properties
      var tilePropertiesBag = oTileApi.bag.getBag('tileProperties');
      tilePropertiesBag.setText('display_title_text', oConfigToSave.display_title_text);
      tilePropertiesBag.setText('display_subtitle_text', oConfigToSave.display_subtitle_text);
      tilePropertiesBag.setText('display_info_text', oConfigToSave.display_info_text);
      tilePropertiesBag.setText('display_search_keywords', oConfigToSave.display_search_keywords);

      function logErrorAndReject(oError) {
        jQuery.sap.log.error(oError, null, "view.CustomTile.controller");
      }

      // saving the relevant properteis
      tilePropertiesBag.save(
        // success handler
        function() {
          jQuery.sap.log.debug("property bag 'tileProperties' saved successfully");

          // update the local tile's config - saving changes on the Model
          this.getView().getModel().setProperty("/config", oConfigToSave);

          // update tile's model for changes to appear immediately
          // (and not wait for the refresh handler which happens every 10 seconds)
          this.getView().getModel().setProperty('/data/display_title_text', oConfigToSave.display_title_text);
          this.getView().getModel().setProperty('/data/display_subtitle_text', oConfigToSave.display_subtitle_text);
          this.getView().getModel().setProperty('/data/display_info_text', oConfigToSave.display_info_text);

          // call to refresh model which (due to the binding) will refresh the tile
          this.getView().getModel().refresh();
        }.bind(this),
        logErrorAndReject // error handler
      );
    },
    // configuration save handler
    onSaveConfiguration: function(oConfigurationView) {
      var
      // the deferred object required from the configurationUi contract
        oDeferred = jQuery.Deferred(),
        oModel = oConfigurationView.getModel(),
        // tile model placed into configuration model by getConfigurationUi
        oTileModel = oModel.getProperty("/tileModel"),
        oTileApi = oConfigurationView.getViewData().chip,
        aTileNavigationActions = sap.ushell.components.tiles.utils.tileActionsRows2TileActionsArray(oModel.getProperty("/config/tile_actions_rows")),
        // get the configuration to save from the model
        configToSave = {
          //display_icon_url: oModel.getProperty("/config/display_icon_url"),
          display_number_unit: oModel.getProperty("/config/display_number_unit"),
          service_first_label: oModel.getProperty("/config/service_first_label"),
          service_second_label: oModel.getProperty("/config/service_second_label"),
          service_third_label: oModel.getProperty("/config/service_third_label"),
          service_url1: oModel.getProperty("/config/service_url1"),
          service_url2: oModel.getProperty("/config/service_url2"),
          service_url3: oModel.getProperty("/config/service_url3"),
          service_lower_threshold: oModel.getProperty("/config/service_lower_threshold"),
          service_upper_threshold: oModel.getProperty("/config/service_upper_threshold"),
          service_refresh_interval: oModel.getProperty("/config/service_refresh_interval"),
          navigation_use_semantic_object: oModel.getProperty("/config/navigation_use_semantic_object"),
          navigation_target_url: oModel.getProperty("/config/navigation_target_url"),
          navigation_semantic_object: jQuery.trim(oModel.getProperty("/config/navigation_semantic_object")) || "",
          navigation_semantic_action: jQuery.trim(oModel.getProperty("/config/navigation_semantic_action")) || "",
          navigation_semantic_parameters: jQuery.trim(oModel.getProperty("/config/navigation_semantic_parameters")),
          display_search_keywords: oModel.getProperty("/config/display_search_keywords")
        };

      //If the input fields icon, semantic object and action are failing the input validations, then through an error message requesting the user to enter/correct those fields
      var bReject = sap.ushell.components.tiles.utils.checkInputOnSaveConfig(oConfigurationView);
      if (!bReject) {
        bReject = sap.ushell.components.tiles.utils.checkTileActions(oConfigurationView);
      }
      if (bReject) {
        oDeferred.reject("mandatory_fields_missing");
        return oDeferred.promise();
      }
      // overwrite target URL in case of semantic object navigation
      if (configToSave.navigation_use_semantic_object) {
        configToSave.navigation_target_url = sap.ushell.components.tiles.utils.getSemanticNavigationUrl(configToSave);
        oModel.setProperty("/config/navigation_target_url", configToSave.navigation_target_url);
      }

      // use bag contract in order to store translatable properties
      var tilePropertiesBag = oTileApi.bag.getBag('tileProperties');
      tilePropertiesBag.setText('display_title_text', oModel.getProperty("/config/display_title_text"));
      tilePropertiesBag.setText('display_subtitle_text', oModel.getProperty("/config/display_subtitle_text"));
      tilePropertiesBag.setText('display_info_text', oModel.getProperty("/config/display_info_text"));
      tilePropertiesBag.setText('display_search_keywords', configToSave.display_search_keywords);

      var tileNavigationActionsBag = oTileApi.bag.getBag('tileNavigationActions');
      //forward populating of tile navigation actions array into the bag, to Utils
      sap.ushell.components.tiles.utils.populateTileNavigationActionsBag(tileNavigationActionsBag, aTileNavigationActions);

      function logErrorAndReject(oError, oErrorInfo) {
        jQuery.sap.log.error(oError, null, "CustomTile.controller");
        oDeferred.reject(oError, oErrorInfo);
      }

      // use configuration contract to write parameter values
      oTileApi.writeConfiguration.setParameterValues({
          tileConfiguration: JSON.stringify(configToSave)
        },
        // success handler
        function() {
          var oConfigurationConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, false, false),
            // get tile config data in admin mode
            oTileConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, true, false),
            // switching the model under the tile -> keep the tile model
            oModel = new sap.ui.model.json.JSONModel({
              config: oConfigurationConfig,
              // keep tile model
              tileModel: oTileModel
            });

          oConfigurationView.setModel(oModel);

          // update tile model
          oTileModel.setData({
            data: oTileConfig,
            nav: {
              navigation_target_url: ""
            }
          }, false);
          if (oTileApi.preview) {
            oTileApi.preview.setTargetUrl(oConfigurationConfig.navigation_target_url);
            oTileApi.preview.setPreviewIcon(oConfigurationConfig.display_icon_url);
            oTileApi.preview.setPreviewTitle(oConfigurationConfig.display_title_text);
          }

          tilePropertiesBag.save(
            // success handler
            function() {
              jQuery.sap.log.debug("property bag 'tileProperties' saved successfully");
              // update possibly changed values via contracts
              if (oTileApi.title) {
                oTileApi.title.setTitle(
                  configToSave.display_title_text,
                  // success handler
                  function() {
                    oDeferred.resolve();
                  },
                  logErrorAndReject // error handler
                );
              } else {
                oDeferred.resolve();
              }
            },
            logErrorAndReject // error handler
          );

          tileNavigationActionsBag.save(
            // success handler
            function() {
              jQuery.sap.log.debug("property bag 'navigationProperties' saved successfully");
            },
            logErrorAndReject // error handler
          );
        },
        logErrorAndReject // error handler
      );

      return oDeferred.promise();
    },

    successHandleFn: function(index, oResult) {
      var oConfig = this.getView().getModel().getProperty("/config");
      this.oDataRequest = undefined;
      var oData = oResult;
      if (typeof oResult === "object") {
        var uriParamInlinecount = jQuery.sap.getUriParameters(oConfig.service_url1).get("$inlinecount");
        if (uriParamInlinecount && uriParamInlinecount === "allpages") {
          oData = {
            number: oResult.__count
          };
        } else {
          oData = this.extractData(oData);
        }
      } else if (typeof oResult === "string") {
        oData = {
          number: oResult
        };
      }

      var oDataComp = {
        title: "",
        value: "",
        color: ""
      };

      switch (index) {
        case 1:
          oDataComp.title = oConfig.service_first_label;
          break;
        case 2:
          oDataComp.title = oConfig.service_second_label;
          break;
        case 3:
          oDataComp.title = oConfig.service_third_label;
          break;
      }

      oDataComp.value = Number(oData.number);

      if (oDataComp.value > Number(oConfig.service_upper_threshold)) {
        oDataComp.color = "Critical";
      } else if (oDataComp.value < Number(oConfig.service_lower_threshold)) {
        oDataComp.color = "Good";
      } else {
        oDataComp.color = "Neutral";
      }

      // set data to display
      //var oDataToDisplay = sap.ushell.components.tiles.utils.getDataToDisplay(oConfig, oDataComp);
      var oModel = this.getView().getModel();
      var sCompModelProp = oModel.getProperty("/data/comparisonModel");
      if (!sCompModelProp) {
        var oComparisonProp = {
          1: {
            "title": "",
            "value": 0,
            "color": "Neutral"
          },
          2: {
            "title": "",
            "value": 0,
            "color": "Neutral"
          },
          3: {
            "title": "",
            "value": 0,
            "color": "Neutral"
          }
        };
        oModel.setProperty("/data/comparisonModel", oComparisonProp);
      }
      oModel.setProperty("/data/comparisonModel/" + index + "/", oDataComp);

      // rewrite target URL
      this.getView().getModel().setProperty("/nav/navigation_target_url",
        sap.ushell.components.tiles.utils.addParamsToUrl(
          this.navigationTargetUrl,
          oDataToDisplay
        ));
    },

    // error handler
    errorHandlerFn: function(oMessage) {
      var oConfig = this.getView().getModel().getProperty("/config");
      this.oDataRequest1 = undefined;
      this.oDataRequest2 = undefined;
      this.oDataRequest3 = undefined;
      var sMessage = oMessage && oMessage.message ? oMessage.message : oMessage,
        oResourceBundle = sap.ushell.components.tiles.utils.getResourceBundleModel()
        .getResourceBundle();
      if (oMessage.response) {
        sMessage += " - " + oMessage.response.statusCode + " " + oMessage.response.statusText;
      }
      // log in English only
      jQuery.sap.log.error(
        "Failed to update data via service " + oConfig.service_url1 + ": " + sMessage,
        null,
        "view.CustomTile"
      );
      this.getView().getModel().setProperty("/data",
        sap.ushell.components.tiles.utils.getDataToDisplay(oConfig, {
          number: "???",
          info: oResourceBundle.getText("dynamic_data.error"),
          infoState: "Critical"
        })
      );
    },

    // configuration cancel handler
    onCancelConfiguration: function(oConfigurationView, successHandler, errorHandle) {
      // re-load old configuration and display
      var oViewData = oConfigurationView.getViewData(),
        oModel = oConfigurationView.getModel(),
        // tile model placed into configuration model by getConfigurationUi
        oTileModel = oModel.getProperty("/tileModel"),
        oTileApi = oViewData.chip,
        oCurrentConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, false, false);
      oConfigurationView.getModel().setData({
        config: oCurrentConfig,
        tileModel: oTileModel
      }, false);
    },
    // loads data from backend service
    loadData: function(nservice_refresh_interval) {
      var oDynamicTileView = this.getView(),
        oConfig = oDynamicTileView.getModel().getProperty("/config"),
        sUrl1 = oConfig.service_url1,
        sUrl2 = oConfig.service_url2,
        sUrl3 = oConfig.service_url3,
        that = this;
      var oTileApi = this.getView().getViewData().chip;
      if (!sUrl1 && !sUrl2 && !sUrl3) {
        return;
      }
      if (/;o=([;\/?]|$)/.test(sUrl1)) { // URL has placeholder segment parameter ;o=
        sUrl1 = oTileApi.url.addSystemToServiceUrl(sUrl1);
      }
      if (/;o=([;\/?]|$)/.test(sUrl2)) { // URL has placeholder segment parameter ;o=
        sUrl2 = oTileApi.url.addSystemToServiceUrl(sUrl2);
      }
      if (/;o=([;\/?]|$)/.test(sUrl3)) { // URL has placeholder segment parameter ;o=
        sUrl3 = oTileApi.url.addSystemToServiceUrl(sUrl3);
      }
      //set the timer if required
      if (nservice_refresh_interval > 0) {
        // log in English only
        jQuery.sap.log.info(
          "Wait " + nservice_refresh_interval + " seconds before calling " + oConfig.service_url1 + " again",
          null,
          "view.CustomTile.controller"
        );
        // call again later
        this.timer = setTimeout(that.loadData.bind(that, nservice_refresh_interval, false), (nservice_refresh_interval * 1000));
      }

      // Verify the the Tile visibility is "true" in order to issue an oData request
      if (oTileApi.visible.isVisible() && !that.oDataRequest1) {
        this.bIsDataRequested = true;
        that.oDataRequest1 = OData.read({
            requestUri: sUrl1,
            headers: {
              "Cache-Control": "no-cache, no-store, must-revalidate",
              "Pragma": "no-cache",
              "Expires": "0"
            }
          },
          // sucess handler
          this.successHandleFn.bind(this, 1),
          this.errorHandlerFn.bind(this)
        ); // End of oData.read

        that.oDataRequest2 = OData.read({
            requestUri: sUrl2,
            headers: {
              "Cache-Control": "no-cache, no-store, must-revalidate",
              "Pragma": "no-cache",
              "Expires": "0"
            }
          },
          // sucess handler
          this.successHandleFn.bind(this, 2),
          this.errorHandlerFn.bind(this)
        ); // End of oData.read

        that.oDataRequest3 = OData.read({
            requestUri: sUrl3,
            headers: {
              "Cache-Control": "no-cache, no-store, must-revalidate",
              "Pragma": "no-cache",
              "Expires": "0"
            }
          },
          // sucess handler
          this.successHandleFn.bind(this, 3),
          this.errorHandlerFn.bind(this)
        ); // End of oData.read
      }
    },
    // loads data once if not in configuration mode
    refreshHandler: function(oDynamicTileController) {
      var oTileApi = oDynamicTileController.getView().getViewData().chip;
      if (!oTileApi.configurationUi.isEnabled()) {
        oDynamicTileController.loadData(0);
      } else {
        oDynamicTileController.stopRequests();
      }
    },

    // load data in place in case setting visibility from false to true
    // with no additional timer registered
    visibleHandler: function(isVisible) {
      var oView = this.getView(),
        oConfig = oView.getModel().getProperty("/config"),
        nservice_refresh_interval = oConfig.service_refresh_interval;
      if (isVisible) {
        if (!this.bIsDataRequested) {
          //tile is visible and data wasn't requested yet
          this.refreshHandler(this);
        }
        if (nservice_refresh_interval) {
          //tile is visible and the refresh interval isn't set to 0
          this.refreshHandler(this);
        }
      } else {
        this.stopRequests();
      }
    }

  });
}());
(function () {
    "use strict";
    /*global jQuery, sap */
    jQuery.sap.require("sap.ushell.components.tiles.utils");
    sap.ui.controller("view.Configuration", {

        // checks given inputs
        onConfigurationInputChange: function (oControlEvent) {
            sap.ushell.components.tiles.utils.checkInput(this.getView(), oControlEvent);
        },
        // default semantic objects for dynamic applauncher: blank
        aDefaultObjects : [{obj: "", name: ""}],
       onInit: function () {
            var oView = this.getView(),
                oSemanticObjectSelector = oView.byId("navigation_semantic_objectInput"),
                oActionSelector = oView.byId("navigation_semantic_actionInput"),
                oResourceModel = sap.ushell.components.tiles.utils.getResourceBundleModel();

            oView.setModel(oResourceModel, "i18n");
            var oBundle = oResourceModel.getResourceBundle();
            // set view name for identification in utils
            oView.setViewName("view.Configuration");
            sap.ushell.components.tiles.utils.createSemanticObjectModel(this, oSemanticObjectSelector, this.aDefaultObjects);
            sap.ushell.components.tiles.utils.createActionModel(this, oActionSelector, this.aDefaultObjects);

            // make sure that the chose object is written back to the configuration
            oSemanticObjectSelector.attachChange(function (oControlEvent) {
                var sValue = oControlEvent.getSource().getValue();
                oView.getModel().setProperty("/config/navigation_semantic_object", sValue);
            });
            oActionSelector.attachChange(function (oControlEvent) {
                var sValue = oControlEvent.getSource().getValue();
                oView.getModel().setProperty("/config/navigation_semantic_action", sValue);
            });
            // toggle editable property of targetURL input field depending on navigation_use_semantic_object
            oView.byId("targetUrl").bindProperty("enabled", {
                formatter: function (bUseLaunchpad) {
                    return !bUseLaunchpad;
                },
                path: "/config/navigation_use_semantic_object"
            });
            //Adding list items URL and Intent to the Target Type in Tile Actions section
            var oItem = new sap.ui.core.ListItem({key: "URL", text:oBundle.getText("configuration.tile_actions.table.target_type.url")});
            oView.byId("targetTypeCB").addItem(oItem);
            oItem = new sap.ui.core.ListItem({key: "INT", text:oBundle.getText("configuration.tile_actions.table.target_type.intent")});
            oView.byId("targetTypeCB").addItem(oItem);

        },

        onAfterRendering: function(){
//            sap.ushell.components.tiles.utils.updateTooltipForDisabledProperties(this.getView());
            sap.ushell.components.tiles.utils.updateMessageStripForOriginalLanguage(this.getView());
        },

        // forward semantic object value helper request to utils
        onValueHelpRequest : function (oEvent) {
            //Third parameter is to differentiate whether it's Tile Actions icon field or general icon field. If it's true, then it's tile actions icon field, else general icon field.
            sap.ushell.components.tiles.utils.objectSelectOnValueHelpRequest(this, oEvent, false);
        },
        // forward semantic action value helper request to utils
        onActionValueHelpRequest : function (oEvent) {
            //Third parameter is to differentiate whether it's Tile Actions icon field or general icon field. If it's true, then it's tile actions icon field, else general icon field.
            sap.ushell.components.tiles.utils.actionSelectOnValueHelpRequest(this, oEvent, false);
        },
        // change handler for check box
        onCheckBoxChange : function (oEvent) {
            var oView = this.getView(),
                oSemanticObjectSelector = oView.byId("navigation_semantic_objectInput"),
                oModel = oSemanticObjectSelector.getModel(),
                value = oEvent.getSource().getSelected();
            oModel.setProperty("/enabled", value);
            sap.ushell.components.tiles.utils.checkInput(this.getView(), oEvent);
        },
        // forward icon value help request to utils
        onIconValueHelpRequest : function (oEvent) {
            //Third parameter is to differentiate whether it's Tile Actions icon field or general icon field. If it's true, then it's tile actions icon field, else general icon field.
            sap.ushell.components.tiles.utils.iconSelectOnValueHelpRequest(this, oEvent, false);
        },
        // forward icon close request to utils
        onSelectIconClose: function () {
            sap.ushell.components.tiles.utils.onSelectIconClose(this.getView());
        },
        // forward icon ok to utils
        onSelectIconOk: function () {
            sap.ushell.components.tiles.utils.onSelectIconOk(this.getView());
        },
        //This function applies table logic for the Action according to the Target Type:
        //if Taregt Type is URL, then Action field should be disabled else if it's Intent, then the Action field should be enabled.
        handleTargetTypeChange : function(oTargetTypeComboBox){
            sap.ushell.components.tiles.utils.onTargetTypeChange(oTargetTypeComboBox);
        },
        //forward tile actions semantic object value helper request to utils
        onTileActionValueHelp : function (oEvent) {
            //Third parameter is to differentiate whether it's Tile Actions icon field or general icon field. If it's true, then it's tile actions icon field, else general icon field.
            sap.ushell.components.tiles.utils.objectSelectOnValueHelpRequest(this, oEvent, true);
        },
        //forward icon value help request to utils
        onTileActionIconValueHelp : function (oEvent) {
            //Third parameter is to differentiate whether it's Tile Actions icon field or general icon field. If it's true, then it's tile actions icon field, else general icon field.
            sap.ushell.components.tiles.utils.iconSelectOnValueHelpRequest(this, oEvent, true);
        },
        //adds new row in the tile actions table
        addRow : function(){
            sap.ushell.components.tiles.utils.addTileActionsRow(this.getView());
        },
        //delets row in the tile actions table
        deleteRow : function(){
            sap.ushell.components.tiles.utils.deleteTileActionsRow(this.getView());
       }
    });
}());
<core:View 
  xmlns="sap.m" 
  xmlns:core="sap.ui.core" 
  xmlns:form="sap.ui.layout.form" 
  xmlns:layout="sap.ui.layout" 
  xmlns:table="sap.ui.table" 
  xmlns:clayout="sap.ui.commons.layout" 
  xmlns:common="sap.ui.commons"
  controllerName="view.Configuration">
  <MessageStrip 
    id="messageStrip" 
    showIcon="true" 
    showCloseButton="false" 
    visible="false"/>
  <form:SimpleForm 
    id="configuration" 
    maxContainerCols="8" 
    minWidth="1024" 
    editable="true">
    <form:content>

      <core:Title id="categoryCommon" text="{i18n>configuration.category.general}"/>
      
      <Label text="{i18n>configuration.display_title_text}"/>
      <Input id="titleInput" value="{/config/display_title_text}" width="100%" tooltip="{i18n>configuration.display_title_text.tooltip}" enabled="{/config/editable}" editable="{/config/isLocaleSuitable}"/>
      
      <Label text="{i18n>configuration.display_subtitle_text}"/>
      <Input id="subtitleInput" value="{/config/display_subtitle_text}" enabled="{/config/editable}" tooltip="{i18n>configuration.display_subtitle_text.tooltip}" editable="{/config/isLocaleSuitable}"/>
      
      <Label text="{i18n>configuration.keywords}"/>
      <Input id="keywordsInput" value="{/config/display_search_keywords}" enabled="{/config/editable}" width="100%" tooltip="{i18n>configuration.keywords.tooltip}" editable="{/config/isLocaleSuitable}"/>
      
      <!--<Label text="{i18n>configuration.display_icon_url}"/>
      <Input id="iconInput" value="{/config/display_icon_url}" enabled="{/config/editable}" placeholder="sap-icon://inbox" tooltip="{i18n>configuration.display_icon_url.tooltip}" liveChange="onConfigurationInputChange"
valueHelpRequest="onIconValueHelpRequest" showValueHelp="true"/>-->
      
      <Label text="{i18n>configuration.display_info_text}"/>
      <Input id="infoInput" value="{/config/display_info_text}" width="100%" enabled="{/config/editable}" tooltip="{i18n>configuration.display_info_text.tooltip}" editable="{/config/isLocaleSuitable}"/>
      
      <core:Title id="categoryDynamicData" text="{i18n>configuration.category.dynamic_data}"/>

      <Label text="Service Label(1)"/>
      <Input id="serviceLabel1Input" value="{/config/service_first_label}" width="100%" enabled="{/config/editable}" tooltip="Service Label(1)"/>
      <Label text="Service URL(1)"/>
      <Input id="serviceUrl1Input" value="{/config/service_url1}" width="100%" enabled="{/config/editable}" tooltip="Service URL(1)"/>

      <Label text="Service Label(2)"/>
      <Input id="serviceLabel2Input" value="{/config/service_second_label}" width="100%" enabled="{/config/editable}" tooltip="Service Label(2)"/>
      <Label text="Service URL(2)"/>
      <Input id="serviceUrl2Input" value="{/config/service_url2}" width="100%" enabled="{/config/editable}" tooltip="Service URL(2)"/>

      <Label text="Service Label(3)"/>
      <Input id="serviceLabel3Input" value="{/config/service_third_label}" width="100%" enabled="{/config/editable}" tooltip="Service Label(3)"/>
      <Label text="Service URL(3)"/>
      <Input id="serviceUrl3Input" value="{/config/service_url3}" width="100%" enabled="{/config/editable}" tooltip="Service URL(3)"/>

      <Label text="Lower Threshold"/>
      <Input id="lowerThresholdInput" value="{/config/service_lower_threshold}" type="Number" enabled="{/config/editable}" width="6em" placeholder="{i18n>configuration.lower_threshold}" tooltip="{i18n>configuration.lower_threshold.tooltip}"/>

      <Label text="Upper Threshold"/>
      <Input id="upperThresholdInput" value="{/config/service_upper_threshold}" type="Number" enabled="{/config/editable}" width="6em" placeholder="{i18n>configuration.upper_threshold}" tooltip="{i18n>configuration.upper_threshold.tooltip}"/>

      <Label text="{i18n>configuration.service_refresh_interval}"/>
      <Input id="refreshInput" value="{/config/service_refresh_interval}" type="Number" enabled="{/config/editable}" width="6em" placeholder="{i18n>configuration.seconds}" tooltip="{i18n>configuration.seconds.tooltip}"/>


      <core:Title id="categoryNavigation" text="{i18n>configuration.category.navigation}"/>
      
      <Label text="{i18n>configuration.navigation_use_semantic_object}"/>
      <CheckBox id="useLpdCheckbox" selected="{/config/navigation_use_semantic_object}" select="onCheckBoxChange" tooltip="{i18n>configuration.navigation_use_semantic_object.tooltip}" enabled="{/config/editable}"/>
      
      <Label text="{i18n>configuration.semantic_object}"/>
      <Input id="navigation_semantic_objectInput" width="100%" tooltip="{i18n>configuration.semantic_object.tooltip}" maxLength="30" liveChange="onConfigurationInputChange" valueHelpRequest="onValueHelpRequest" showValueHelp="true" showSuggestion="true"
enabled="{= ${/enabled} &amp;&amp; ${/config/editable}}" value="{/value}"/>
      
      <Label text="{i18n>configuration.navigation_semantic_action}"/>
      <Input id="navigation_semantic_actionInput" value="{/config/navigation_semantic_action}" width="100%" maxLength="50" enabled="{= ${/config/navigation_use_semantic_object} &amp;&amp; ${/config/editable}}"
tooltip="{i18n>configuration.navigation_semantic_action.tooltip}" liveChange="onConfigurationInputChange" valueHelpRequest="onActionValueHelpRequest" showValueHelp="true" showSuggestion="true"/>
      
      <Label text="{i18n>configuration.navigation_semantic_parameters}"/>
      <Input id="navigation_semantic_parametersInput" value="{/config/navigation_semantic_parameters}" width="100%" enabled="{= ${/config/navigation_use_semantic_object} &amp;&amp; ${/config/editable}}"
tooltip="{i18n>configuration.navigation_semantic_parameters.tooltip}"/>
      
      <Label text="{i18n>configuration.navigation_target_url}"/>
      <Input id="targetUrl" value="{/config/navigation_target_url}" type="Url" width="100%" tooltip="{i18n>configuration.navigation_target_url.tooltip}"/>

      
      <core:Title id="categoryTileActions" text="{i18n>configuration.category.tile_actions}"/>
      
      <table:Table id="tileActions" rows="{/config/tile_actions_rows}" selectionBehavior="Row" selectionMode="Multi" visibleRowCount="3" enableColumnReordering="false" rowHeight="30px">
        <table:Column id="menuItem" width="80px" tooltip="{i18n>configuration.tile_actions.table.menu_item_tooltip}">
          <Label text="{i18n>configuration.tile_actions.table.menu_item}"/>
          <table:template>
            <common:TextField value="{menu_title}" enabled="{editable}" valueState="{valueState}"/>
          </table:template>
        </table:Column>
        <table:Column id="targetType" width="85px" tooltip="{i18n>configuration.tile_actions.table.target_type_tooltip}">
          <Label text="{i18n>configuration.tile_actions.table.target_type}"/>
          <table:template>
            <common:ComboBox id="targetTypeCB" value="{target_type}" enabled="{editable}" change="handleTargetTypeChange"/>
            </table:template>
          </table:Column>
        <table:Column id="navigationTarget" width="162px" tooltip="{i18n>configuration.tile_actions.table.navigation_target_tooltip}">
          <Label text="{i18n>configuration.tile_actions.table.navigation_target}"/>
          <table:template>
            <Input liveChange="onConfigurationInputChange" valueHelpRequest="onTileActionValueHelp" showValueHelp="{isTargetTypeIntent}" showSuggestion="{isTargetTypeIntent}" value="{navigation_target}" enabled="{editable}"/>
          </table:template>
        </table:Column>
        <table:Column id="action" width="85px" tooltip="{i18n>configuration.tile_actions.table.action_tooltip}">
          <Label text="{i18n>configuration.tile_actions.table.action}"/>
          <table:template>
            <common:TextField value="{action}" enabled="{isTargetTypeIntent}"/>
          </table:template>
        </table:Column>
        <table:Column id="icon" width="110px" tooltip="{i18n>configuration.tile_actions.table.icon_tooltip}">
          <Label text="{i18n>configuration.tile_actions.table.icon}"/>
          <table:template>
            <Input value="{icon}" placeholder="sap-icon://inbox" enabled="{/config/editable}" valueState="{iconValueState}" valueStateText="{iconValueStateText}" liveChange="onConfigurationInputChange" valueHelpRequest="onTileActionIconValueHelp"
          showValueHelp="true"/>
          </table:template>
        </table:Column>
      </table:Table>
      
      <Label/>
      <clayout:MatrixLayout>
        <clayout:MatrixLayoutRow>
          <clayout:MatrixLayoutCell hAlign="End">
            <common:Button id="addRow" text="{i18n>configuration.tile_actions.table.add}" enabled="{/config/editable}" tooltip="{i18n>configuration.tile_actions.table.add_tooltip}" press="addRow" width="100px"/>
            <common:Button id="deleteRow" text="{i18n>configuration.tile_actions.table.remove}" enabled="{/config/editable}" tooltip="{i18n>configuration.tile_actions.table.remove_tooltip}" press="deleteRow" width="100px"/>
          </clayout:MatrixLayoutCell>
        </clayout:MatrixLayoutRow>
      </clayout:MatrixLayout>
    </form:content>
  </form:SimpleForm>
  <HBox visible="false">
    <Dialog id="selectIconDialog" leftButton="ok" rightButton="cancel" title="{i18n>configuration.select_icon}">
      <content>
        <layout:ResponsiveFlowLayout id="icons"/>
        <HBox visible="true">
          <Button id="ok" enabled="{/config/ok.enabled}" text="{i18n>configuration.ok}"/>
          <Button id="cancel" text="{i18n>configuration.cancel}" press="onSelectIconClose"/>
        </HBox>
      </content>
    </Dialog>
  </HBox>
</core:View>
<?xml version="1.0" encoding="UTF-8"?>
<chip xmlns="http://schemas.sap.com/sapui2/services/Chip/1">
    <implementation>
        <sapui5>
            <viewName>view/CustomTile.view.xml</viewName>
        </sapui5>
    </implementation>
    <appearance>
        <title>Custom Dynamic Applauncher</title>
        <description>Custom Dynamic Applauncher Chip</description>
    </appearance>
    <contracts>
        <consume id="bag" />
        <consume id="configuration">
            <parameters>
                <parameter name="tileConfiguration"></parameter>
            </parameters>
        </consume>
        <consume id="writeConfiguration" />
        <consume id="configurationUi" />
        <consume id="preview" />
        <consume id="refresh" />
        <consume id="search" />
        <consume id="url" />
        <consume id="visible" />
        <consume id="actions" />
        <consume id="types">
            <parameters>
                <parameter name="supportedTypes">tile,link</parameter>
            </parameters>
        </consume>
    </contracts>
    <parameters>
      <parameter name="/UI2/ChipType">applauncher</parameter>
    </parameters>
</chip>
(function() {
  "use strict";
  /*global jQuery, OData, sap, setTimeout, hasher */
  jQuery.sap.require("sap.ui.core.IconPool");
  jQuery.sap.require("sap.ushell.components.tiles.utils");

  sap.ui.controller("demo.CustomTile", {
    // handle to control/cancel browser's setTimeout()
    timer: null,
    // handle to control/cancel data.js OData.read()
    oDataRequest: null,
    _setComparisonData: function(oModel) {

      var aData = {
        1: {
          "title": "Example 1",
          "value": 25,
          "color": "Neutral"
        },
        2: {
          "title": "Example 2",
          "value": 15,
          "color": "Good"
        },
        3: {
          "title": "Example 3",
          "value": 35,
          "color": "Critical"
        }
      };

      oModel.setProperty("/comparisonModel", aData);

    },
    onInit: function() {
      var oView = this.getView();
      var oViewData = oView.getViewData();
      var that = this;

      var oModel = new sap.ui.model.json.JSONModel();
      oModel.setProperty("/display_title_text", "Test Title");
      oModel.setProperty("/display_subtitle_text", "Test Subtitle");
      oModel.setProperty("/display_icon_url", "icon");
      oModel.setProperty("/display_info_text", "wide view");
      this._setComparisonData(oModel);
      oView.setModel(oModel);

    },
    // convenience function to stop browser's timeout and OData calls
    stopRequests: function() {
      if (this.timer) {
        clearTimeout(this.timer);
      }
      if (this.oDataRequest) {
        try {
          this.oDataRequest.abort();
        } catch (e) {
          jQuery.sap.log.warning(e.name, e.message);
        }
      }
    },
    // destroy handler stops requests
    onExit: function() {
      this.stopRequests();
    },
    // trigger to show the configuration UI if the tile is pressed in Admin mode
    onPress: function() {
      var oView = this.getView(),
        oViewData = oView.getViewData(),
        oModel = oView.getModel(),
        sTargetUrl = oModel.getProperty("/nav/navigation_target_url"),
        oTileApi = oViewData.chip;
      if (oTileApi.configurationUi.isEnabled()) {
        oTileApi.configurationUi.display();
      } else if (sTargetUrl) {
        if (sTargetUrl[0] === '#') {
          hasher.setHash(sTargetUrl);
        } else {
          window.open(sTargetUrl, '_blank');
        }
      }
    },
    // dynamic data updater
    onUpdateDynamicData: function() {
      var oView = this.getView(),
        oConfig = oView.getModel().getProperty("/config"),
        nservice_refresh_interval = oConfig.service_refresh_interval;
      if (!nservice_refresh_interval) {
        nservice_refresh_interval = 0;
      } else if (nservice_refresh_interval < 10) {
        // log in English only
        jQuery.sap.log.warning(
          "Refresh Interval " + nservice_refresh_interval + " seconds for service URL " + oConfig.service_url1 + " is less than 10 seconds, which is not supported. " + "Increased to 10 seconds automatically.",
          null,
          "view.CustomTile.controller"
        );
        nservice_refresh_interval = 10;
      }
      if (oConfig.service_url1) {
        this.loadData(nservice_refresh_interval);
      }
    },
    extractData: function(oData) {
      var name,
        aKeys = ["results", "icon", "title", "number", "numberUnit", "info", "infoState", "infoStatus", "targetParams", "subtitle", "stateArrow", "numberState", "numberDigits", "numberFactor"];

      if (typeof oData === "object" && Object.keys(oData).length === 1) {
        name = Object.keys(oData)[0];
        if (jQuery.inArray(name, aKeys) === -1) {
          return oData[name];
        }
      }
      return oData;
    },
    // tile settings action UI save handler
    onSaveRuntimeSettings: function(oSettingsView) {
      var
        oViewModel = oSettingsView.getModel(),
        oTileApi = this.getView().getViewData().chip,
        oConfigToSave = this.getView().getModel().getProperty("/config");

      oConfigToSave.display_title_text = oViewModel.getProperty('/title');
      oConfigToSave.display_subtitle_text = oViewModel.getProperty('/subtitle');
      oConfigToSave.display_info_text = oViewModel.getProperty('/info');
      oConfigToSave.display_search_keywords = oViewModel.getProperty('/keywords');

      // use bag contract in order to store translatable properties
      var tilePropertiesBag = oTileApi.bag.getBag('tileProperties');
      tilePropertiesBag.setText('display_title_text', oConfigToSave.display_title_text);
      tilePropertiesBag.setText('display_subtitle_text', oConfigToSave.display_subtitle_text);
      tilePropertiesBag.setText('display_info_text', oConfigToSave.display_info_text);
      tilePropertiesBag.setText('display_search_keywords', oConfigToSave.display_search_keywords);

      function logErrorAndReject(oError) {
        jQuery.sap.log.error(oError, null, "view.CustomTile.controller");
      }

      // saving the relevant properteis
      tilePropertiesBag.save(
        // success handler
        function() {
          jQuery.sap.log.debug("property bag 'tileProperties' saved successfully");

          // update the local tile's config - saving changes on the Model
          this.getView().getModel().setProperty("/config", oConfigToSave);

          // update tile's model for changes to appear immediately
          // (and not wait for the refresh handler which happens every 10 seconds)
          this.getView().getModel().setProperty('/data/display_title_text', oConfigToSave.display_title_text);
          this.getView().getModel().setProperty('/data/display_subtitle_text', oConfigToSave.display_subtitle_text);
          this.getView().getModel().setProperty('/data/display_info_text', oConfigToSave.display_info_text);

          // call to refresh model which (due to the binding) will refresh the tile
          this.getView().getModel().refresh();
        }.bind(this),
        logErrorAndReject // error handler
      );
    },
    // configuration save handler
    onSaveConfiguration: function(oConfigurationView) {
      var
      // the deferred object required from the configurationUi contract
        oDeferred = jQuery.Deferred(),
        oModel = oConfigurationView.getModel(),
        // tile model placed into configuration model by getConfigurationUi
        oTileModel = oModel.getProperty("/tileModel"),
        oTileApi = oConfigurationView.getViewData().chip,
        aTileNavigationActions = sap.ushell.components.tiles.utils.tileActionsRows2TileActionsArray(oModel.getProperty("/config/tile_actions_rows")),
        // get the configuration to save from the model
        configToSave = {
          display_icon_url: oModel.getProperty("/config/display_icon_url"),
          service_first_label: oModel.getProperty("/config/service_first_label"),
          service_second_label: oModel.getProperty("/config/service_second_label"),
          service_third_label: oModel.getProperty("/config/service_third_label"),
          service_url1: oModel.getProperty("/config/service_url1"),
          service_url2: oModel.getProperty("/config/service_url2"),
          service_url3: oModel.getProperty("/config/service_url3"),
          service_lower_threshold: oModel.getProperty("/config/service_lower_threshold"),
          service_upper_threshold: oModel.getProperty("/config/service_upper_threshold"),
          service_refresh_interval: oModel.getProperty("/config/service_refresh_interval"),
          navigation_use_semantic_object: oModel.getProperty("/config/navigation_use_semantic_object"),
          navigation_target_url: oModel.getProperty("/config/navigation_target_url"),
          navigation_semantic_object: jQuery.trim(oModel.getProperty("/config/navigation_semantic_object")) || "",
          navigation_semantic_action: jQuery.trim(oModel.getProperty("/config/navigation_semantic_action")) || "",
          navigation_semantic_parameters: jQuery.trim(oModel.getProperty("/config/navigation_semantic_parameters")),
          display_search_keywords: oModel.getProperty("/config/display_search_keywords")
        };

      //If the input fields icon, semantic object and action are failing the input validations, then through an error message requesting the user to enter/correct those fields
      var bReject = sap.ushell.components.tiles.utils.checkInputOnSaveConfig(oConfigurationView);
      if (!bReject) {
        bReject = sap.ushell.components.tiles.utils.checkTileActions(oConfigurationView);
      }
      if (bReject) {
        oDeferred.reject("mandatory_fields_missing");
        return oDeferred.promise();
      }
      // overwrite target URL in case of semantic object navigation
      if (configToSave.navigation_use_semantic_object) {
        configToSave.navigation_target_url = sap.ushell.components.tiles.utils.getSemanticNavigationUrl(configToSave);
        oModel.setProperty("/config/navigation_target_url", configToSave.navigation_target_url);
      }

      // use bag contract in order to store translatable properties
      var tilePropertiesBag = oTileApi.bag.getBag('tileProperties');
      tilePropertiesBag.setText('display_title_text', oModel.getProperty("/config/display_title_text"));
      tilePropertiesBag.setText('display_subtitle_text', oModel.getProperty("/config/display_subtitle_text"));
      tilePropertiesBag.setText('display_info_text', oModel.getProperty("/config/display_info_text"));
      tilePropertiesBag.setText('display_search_keywords', configToSave.display_search_keywords);

      var tileNavigationActionsBag = oTileApi.bag.getBag('tileNavigationActions');
      //forward populating of tile navigation actions array into the bag, to Utils
      sap.ushell.components.tiles.utils.populateTileNavigationActionsBag(tileNavigationActionsBag, aTileNavigationActions);

      function logErrorAndReject(oError, oErrorInfo) {
        jQuery.sap.log.error(oError, null, "CustomTile.controller");
        oDeferred.reject(oError, oErrorInfo);
      }

      // use configuration contract to write parameter values
      oTileApi.writeConfiguration.setParameterValues({
          tileConfiguration: JSON.stringify(configToSave)
        },
        // success handler
        function() {
          var oConfigurationConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, false, false),
            // get tile config data in admin mode
            oTileConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, true, false),
            // switching the model under the tile -> keep the tile model
            oModel = new sap.ui.model.json.JSONModel({
              config: oConfigurationConfig,
              // keep tile model
              tileModel: oTileModel
            });

          oConfigurationView.setModel(oModel);

          // update tile model
          oTileModel.setData({
            data: oTileConfig,
            nav: {
              navigation_target_url: ""
            }
          }, false);
          if (oTileApi.preview) {
            oTileApi.preview.setTargetUrl(oConfigurationConfig.navigation_target_url);
            oTileApi.preview.setPreviewIcon(oConfigurationConfig.display_icon_url);
            oTileApi.preview.setPreviewTitle(oConfigurationConfig.display_title_text);
          }

          tilePropertiesBag.save(
            // success handler
            function() {
              jQuery.sap.log.debug("property bag 'tileProperties' saved successfully");
              // update possibly changed values via contracts
              if (oTileApi.title) {
                oTileApi.title.setTitle(
                  configToSave.display_title_text,
                  // success handler
                  function() {
                    oDeferred.resolve();
                  },
                  logErrorAndReject // error handler
                );
              } else {
                oDeferred.resolve();
              }
            },
            logErrorAndReject // error handler
          );

          tileNavigationActionsBag.save(
            // success handler
            function() {
              jQuery.sap.log.debug("property bag 'navigationProperties' saved successfully");
            },
            logErrorAndReject // error handler
          );
        },
        logErrorAndReject // error handler
      );

      return oDeferred.promise();
    },

    successHandleFn: function(index, oResult) {
      var oConfig = this.getView().getModel().getProperty("/config");
      this.oDataRequest = undefined;
      var oData = oResult;
      if (typeof oResult === "object") {
        var uriParamInlinecount = jQuery.sap.getUriParameters(oConfig.service_url1).get("$inlinecount");
        if (uriParamInlinecount && uriParamInlinecount === "allpages") {
          oData = {
            number: oResult.__count
          };
        } else {
          oData = this.extractData(oData);
        }
      } else if (typeof oResult === "string") {
        oData = {
          number: oResult
        };
      }

      var oConfig = this.getView().getModel().getProperty("/config");

      var oDataComp = {
        title: "",
        value: "",
        color: ""
      };

      switch (index) {
        case 1:
          oDataComp.title = oConfig.service_first_label;
          break;
        case 2:
          oDataComp.title = oConfig.service_second_label;
          break;
        case 3:
          oDataComp.title = oConfig.service_third_label;
          break;
      }

      oDataComp.value = Number(oData.number);

      if (oDataComp.value > Number(oConfig.service_upper_threshold)) {
        oDataComp.color = "Critical";
      } else if (oDataComp.value < Number(oConfig.service_lower_threshold)) {
        oDataComp.color = "Good";
      } else {
        oDataComp.color = "Neutral";
      }

      // set data to display
      //var oDataToDisplay = sap.ushell.components.tiles.utils.getDataToDisplay(oConfig, oDataComp);
      var oModel = this.getView().getModel();
      var sCompModelProp = oModel.getProperty("/data/comparisonModel");
      if (!sCompModelProp) {
        var oComparisonProp = {
          1: {
            "title": "",
            "value": 0,
            "color": "Neutral"
          },
          2: {
            "title": "",
            "value": 0,
            "color": "Neutral"
          },
          3: {
            "title": "",
            "value": 0,
            "color": "Neutral"
          }
        };
        oModel.setProperty("/data/comparisonModel", oComparisonProp);
      };
      oModel.setProperty("/data/comparisonModel/" + index + "/", oDataComp);

      // rewrite target URL
      this.getView().getModel().setProperty("/nav/navigation_target_url",
        sap.ushell.components.tiles.utils.addParamsToUrl(
          this.navigationTargetUrl,
          oDataToDisplay
        ));
    },

    // error handler
    errorHandlerFn: function(oMessage) {
      var oConfig = this.getView().getModel().getProperty("/config");
      this.oDataRequest1 = undefined;
      this.oDataRequest2 = undefined;
      this.oDataRequest3 = undefined;
      var sMessage = oMessage && oMessage.message ? oMessage.message : oMessage,
        oResourceBundle = sap.ushell.components.tiles.utils.getResourceBundleModel()
        .getResourceBundle();
      if (oMessage.response) {
        sMessage += " - " + oMessage.response.statusCode + " " + oMessage.response.statusText;
      }
      // log in English only
      jQuery.sap.log.error(
        "Failed to update data via service " + oConfig.service_url1 + ": " + sMessage,
        null,
        "view.CustomTile"
      );
      this.getView().getModel().setProperty("/data",
        sap.ushell.components.tiles.utils.getDataToDisplay(oConfig, {
          number: "???",
          info: oResourceBundle.getText("dynamic_data.error"),
          infoState: "Critical"
        })
      );
    },

    // configuration cancel handler
    onCancelConfiguration: function(oConfigurationView, successHandler, errorHandle) {
      // re-load old configuration and display
      var oViewData = oConfigurationView.getViewData(),
        oModel = oConfigurationView.getModel(),
        // tile model placed into configuration model by getConfigurationUi
        oTileModel = oModel.getProperty("/tileModel"),
        oTileApi = oViewData.chip,
        oCurrentConfig = sap.ushell.components.tiles.utils.getConfiguration(oTileApi, false, false);
      oConfigurationView.getModel().setData({
        config: oCurrentConfig,
        tileModel: oTileModel
      }, false);
    },
    // loads data from backend service
    loadData: function(nservice_refresh_interval) {
      var oDynamicTileView = this.getView(),
        oConfig = oDynamicTileView.getModel().getProperty("/config"),
        sUrl1 = oConfig.service_url1,
        sUrl2 = oConfig.service_url2,
        sUrl3 = oConfig.service_url3,
        that = this;
      var oTileApi = this.getView().getViewData().chip;
      if (!sUrl1 && !sUrl2 && !sUrl3) {
        return;
      }
      if (/;o=([;\/?]|$)/.test(sUrl1)) { // URL has placeholder segment parameter ;o=
        sUrl1 = oTileApi.url.addSystemToServiceUrl(sUrl1);
      }
      if (/;o=([;\/?]|$)/.test(sUrl2)) { // URL has placeholder segment parameter ;o=
        sUrl2 = oTileApi.url.addSystemToServiceUrl(sUrl2);
      }
      if (/;o=([;\/?]|$)/.test(sUrl3)) { // URL has placeholder segment parameter ;o=
        sUrl3 = oTileApi.url.addSystemToServiceUrl(sUrl3);
      }
      //set the timer if required
      if (nservice_refresh_interval > 0) {
        // log in English only
        jQuery.sap.log.info(
          "Wait " + nservice_refresh_interval + " seconds before calling " + oConfig.service_url1 + " again",
          null,
          "view.CustomTile.controller"
        );
        // call again later
        this.timer = setTimeout(that.loadData.bind(that, nservice_refresh_interval, false), (nservice_refresh_interval * 1000));
      }

      // Verify the the Tile visibility is "true" in order to issue an oData request
      if (oTileApi.visible.isVisible() && !that.oDataRequest1) {
        this.bIsDataRequested = true;
        that.oDataRequest1 = OData.read({
            requestUri: sUrl1,
            headers: {
              "Cache-Control": "no-cache, no-store, must-revalidate",
              "Pragma": "no-cache",
              "Expires": "0"
            }
          },
          // sucess handler
          this.successHandleFn.bind(this, 1),
          this.errorHandlerFn.bind(this)
        ); // End of oData.read

        that.oDataRequest2 = OData.read({
            requestUri: sUrl2,
            headers: {
              "Cache-Control": "no-cache, no-store, must-revalidate",
              "Pragma": "no-cache",
              "Expires": "0"
            }
          },
          // sucess handler
          this.successHandleFn.bind(this, 2),
          this.errorHandlerFn.bind(this)
        ); // End of oData.read

        that.oDataRequest3 = OData.read({
            requestUri: sUrl3,
            headers: {
              "Cache-Control": "no-cache, no-store, must-revalidate",
              "Pragma": "no-cache",
              "Expires": "0"
            }
          },
          // sucess handler
          this.successHandleFn.bind(this, 3),
          this.errorHandlerFn.bind(this)
        ); // End of oData.read
      }
    },
    // loads data once if not in configuration mode
    refreshHandler: function(oDynamicTileController) {
      var oTileApi = oDynamicTileController.getView().getViewData().chip;
      if (!oTileApi.configurationUi.isEnabled()) {
        oDynamicTileController.loadData(0);
      } else {
        oDynamicTileController.stopRequests();
      }
    },

    // load data in place in case setting visibility from false to true
    // with no additional timer registered
    visibleHandler: function(isVisible) {
      var oView = this.getView(),
        oConfig = oView.getModel().getProperty("/config"),
        nservice_refresh_interval = oConfig.service_refresh_interval;
      if (isVisible) {
        if (!this.bIsDataRequested) {
          //tile is visible and data wasn't requested yet
          this.refreshHandler(this);
        }
        if (nservice_refresh_interval) {
          //tile is visible and the refresh interval isn't set to 0
          this.refreshHandler(this);
        }
      } else {
        this.stopRequests();
      }
    }

  });
}());