﻿/*global define,require,alert,dojo,$,window,moment*/
/*jslint browser:true,sloppy:true,nomen:true,unparam:true,plusplus:true,indent:4*/
/*
 | Copyright 2014 Esri
 |
 | Licensed under the Apache License, Version 2.0 (the "License");
 | you may not use this file except in compliance with the License.
 | You may obtain a copy of the License at
 |
 |    http://www.apache.org/licenses/LICENSE-2.0
 |
 | Unless required by applicable law or agreed to in writing, software
 | distributed under the License is distributed on an "AS IS" BASIS,
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | See the License for the specific language governing permissions and
 | limitations under the License.
 */
define([
    "dojo/_base/declare",
    "dojo/_base/kernel",
    "dojo/_base/lang",
    "dojo/date/locale",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dojo/dom-construct",
    "dojo/dom-class",
    "dojo/on",
    "dojo/has",
    "dojo/dom-attr",
    "dojo/_base/array",
    "dojo/dom",
    "dojo/touch",
    "dojo/dom-geometry",
    "dojo/dom-style",
    "dojo/query",
    "dojo/window",
    "dojo/Deferred",
    "dojo/text!./templates/geo-form.html",
    "dojo/string",
    "dojo/date/locale",
    "esri/layers/GraphicsLayer",
    "esri/graphic",
    "esri/toolbars/draw",
    "esri/geometry/webMercatorUtils",
    "esri/symbols/SimpleLineSymbol",
    "esri/symbols/SimpleFillSymbol",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/geometry/Polygon",
    "esri/geometry/Point",
    "widgets/locator/locator",
    "widgets/help/help",
    "widgets/bootstrapmap/bootstrapmap"
], function (declare, kernel, lang, dateLocale, _WidgetBase, _TemplatedMixin, domConstruct, domClass, on, has, domAttr, array, dom, touch, domGeom, domStyle, query, dojowindow, Deferred, dijitTemplate, string, locale, GraphicsLayer, Graphic, Draw, webMercatorUtils, SimpleLineSymbol, SimpleFillSymbol, SimpleMarkerSymbol, Polygon, Point, Locator, Help, BootstrapMap) {
    return declare([_WidgetBase, _TemplatedMixin], {
        templateString: dijitTemplate,
        lastWebMapSelected: "",
        sortedFields: [],
        toolbar: null,
        rangeHelpText: null,
        layerTitle: null,
        currentLocation: null,
        defaultValueArray: [],
        fileAttachmentList: null,
        _fileInputIcon: null,
        _fileAttachmentCounter: 1,
        _layerHasReportedByField: false,
        _totalFileAttachedCounter: 0,
        _fileAttachedCounter: 0,
        _fileFailedCounter: 0,
        tooltipHandler: null,
        hasLocationField: false,
        firstMapClickPoint: null,
        _webmapResponse: null,
        newLocationFieldValue: null,
        locationFieldLength: null,
        _existingPopupAttachmentsArray: [],
        _deletedAttachmentsPopupArr: [], // to store deleted attachments
        currentGeoformNode: null, // Refers to node for geoform(add/edit)
        componentOrder: [],
        _resizedFiles: [],
        /**
        * This function is called when widget is constructed.
        * @param{object} options to be mixed
        * @param{object} source reference node
        * @memberOf widgets/geo-form/geo-form
        */
        constructor: function (options, srcRefNode) {
            lang.mixin(this, options);
        },

        /**
        * This function is called on startup of widget.
        * @memberOf widgets/geo-form/geo-form
        */
        startup: function () {
            try {
                this.defaultValueArray = [];
                this._resizedFiles = [];
                // Show loading indicator
                this.appUtils.showLoadingIndicator();
                //Reorder form components  as per configuration
                this._reorderFormComponents();
                // Clear previous attachments
                this._clearAttachements();
                this.sortedFields = [];
                //Change ui and configuration based on add/edit feature
                if (this.isEdit) {
                    domConstruct.empty(this.userForm);
                    this._changeGeoformStyles();
                    this._deletedAttachmentsPopupArr = [];
                    this.currentGeoformNode = $('.esriCTItemDetailsContainer');
                } else {
                    this.currentGeoformNode = $('#geoFormBody');
                    //Check if app is running in desktop/tablet mode and hide the map
                    this.setGeoformMapVisibility();
                }
                if (this.isMapRequired) {
                    // Initialize geo form
                    this._init();
                } else {
                    // show only selected layer and remove other layer from Webmap
                    this._filterOperationalLayers(this._webmapResponse.itemInfo.itemData.operationalLayers);
                    // Filters selected layer fields according to Popup info, Data Types and fields not to be shown in GeoForm
                    this._filterLayerFields(this._webmapResponse);
                    // Creates Geoform UI According to Filtered data
                    this._createGeoFormUI();
                }
                //Create location dialog
                this._locationDialog = new Help({
                    "config": this.appConfig,
                    "title": this.appConfig.i18n.geoform.locationDialogTitle,
                    "content": this.appConfig.i18n.geoform.locationDialogContent,
                    "dialog": "location",
                    "showButtons": true,
                    "okButtonText": this.appConfig.i18n.dialog.yesButton,
                    "cancelButtonText": this.appConfig.i18n.dialog.noButton,
                    "appUtils": this.appUtils
                });
                //Listen for dialogs ok and cancel button clicked event
                this._locationDialog.okButtonClicked = lang.hitch(this, function () {
                    this._gotoImageLocation(this.allMetaData);
                });
                this._locationDialog.cancelButtonClicked = lang.hitch(this, function () {
                    this._locationDialog.hideDialog("location");
                    setTimeout(lang.hitch(this, function () {
                        query(".fileInputButton", this.domNode)[0].focus();
                    }), 400);
                });
                //Prevent refreshing of application on ENTER key press
                $(function() {
                    $("form").submit(function() { return false; });
                });
            } catch (err) {
                // Show error message
                this.appUtils.showError(err.message);
            }
        },

        /**
        * This function will be used to check the current order and arrange them
        * @memberOf widgets/geo-form/geo-form
        */
        _reorderFormComponents: function () {
            var configuredOrderObj = {
                "details": this.config.details,
                "attachments": this.config.attachments,
                "location": this.config.location
            };
            this.componentOrder = [];
            this._checkComponentOrder(configuredOrderObj, "Top");
            this._checkComponentOrder(configuredOrderObj, "Middle");
            this._checkComponentOrder(configuredOrderObj, "Bottom");
            this._setComponentOrder();
        },

        /**
        * Check components current configured order and push it in array
        * @memberOf widgets/geo-form/geo-form
        */
        _checkComponentOrder: function (configuredOrderObj, position) {
            var key;
            for (key in configuredOrderObj) {
                if (configuredOrderObj[key] === position) {
                    this.componentOrder.push(key);
                }
            }
        },

        /**
        * Get dom node with the help of section key
        * @memberOf widgets/geo-form/geo-form
        */
        _getNodeFromKey: function (key) {
            var section;
            switch (key) {
            case "details":
                section = this.detailSection;
                break;
            case "attachments":
                section = this.attachmentSection;
                break;
            case "location":
                section = this.locationSection;
                break;
            }
            return section;
        },

        /**
        * This function will be used to set order of components as configured
        * @memberOf widgets/geo-form/geo-form
        */
        _setComponentOrder: function () {
            var node, prevNode;
            array.forEach(this.componentOrder, lang.hitch(this, function (currentNode, index) {
                node = this._getNodeFromKey(currentNode);
                //add component to the first position if index is 0
                if (index === 0) {
                    domConstruct.place(node, this.componentSection, "first");
                    prevNode = node;
                } else {
                    domConstruct.place(node, prevNode, "after");
                    prevNode = node;
                }
            }));
        },

        /**
        * Set geoform map visibility based on device dimensions
        * @memberOf widgets/geo-form/geo-form
        */
        setGeoformMapVisibility: function () {
            if (dojowindow.getBox().w > 768) {
                domStyle.set(this.map_container, "display", "none");
                if (domClass.contains(this.select_location.nextSibling, "errorMessage")) {
                    this._removeErrorNode(this.select_location.nextSibling);
                }
            } else {
                domStyle.set(this.map_container, "display", "block");
                this._resizeMap();
            }
        },

        /**
        * Change geoform styles based on add/edit feature mode
        * @memberOf widgets/geo-form/geo-form
        */
        _changeGeoformStyles: function () {
            domStyle.set(this.formHeader, "display", "none");
            domStyle.set(this.locationSection, "display", "none");
            domStyle.set(this.map_container, "display", "none");
        },

        /**
        * Create map instance, activate draw tool and initialize locator widget
        * @memberOf widgets/geo-form/geo-form
        */
        _init: function () {
            // Create WebMap using selected webmap id and on loading webmap initiate geoform
            BootstrapMap.createWebMap(this.webMapID, "gf-mapDiv", {
                ignorePopups: true,
                scrollWheelZoom: true,
                editable: false
            }).then(lang.hitch(this, function (response) {
                var zoomInBtn, zoomOutBtn;
                // Scroll geoform to top
                this.currentGeoformNode.animate({
                    scrollTop: 0
                }, 1000);
                // Once the map is created we get access to the response which provides map, operational layers, popup info.
                this._webmapResponse = response;
                this.map = response.map;
                this.map.on("click", lang.hitch(this, function (evt) {
                    if (!this.firstMapClickPoint) {
                        this.firstMapClickPoint = evt.mapPoint;
                    }
                    this._clearSubmissionGraphic();
                }));
                // tooltip for zoom in and zoom out button
                zoomInBtn = query('.esriSimpleSliderIncrementButton', dom.byId(this.map.id))[0];
                zoomOutBtn = query('.esriSimpleSliderDecrementButton', dom.byId(this.map.id))[0];
                if (zoomInBtn) {
                    domAttr.set(zoomInBtn, "title", this.appConfig.i18n.map.zoomInTooltip);
                }
                if (zoomOutBtn) {
                    domAttr.set(zoomOutBtn, "title", this.appConfig.i18n.map.zoomOutTooltip);
                }
                domAttr.set(this.closeButton, "title", this.appConfig.i18n.geoform.geoformBackButtonTooltip);
                domAttr.set(this.closeButton, "aria-label", this.appConfig.i18n.geoform.geoformBackButtonTooltip);
                domAttr.set(this.fallBackTextNode, "title", this.appConfig.i18n.geoform.geoformBackButtonTooltip);
                domAttr.set(this.submitButton, "title", this.appConfig.i18n.geoform.submitButtonTooltip);
                domAttr.set(this.submitButton, "aria-label", this.appConfig.i18n.geoform.submitButtonTooltip);
                domAttr.set(this.cancelButton, "title", this.appConfig.i18n.geoform.cancelButtonTooltip);
                domAttr.set(this.cancelButton, "aria-label", this.appConfig.i18n.geoform.cancelButtonTooltip);
                // store default map extent
                this.defaultExtent = this.map.extent;
                // show only selected layer and remove other layer from Webmap
                this._filterOperationalLayers(response.itemInfo.itemData.operationalLayers);
                // Filters selected layer fields according to Popup info, Data Types and fields not to be shown in GeoForm
                this._filterLayerFields(response);
                // Creates Geoform UI According to Filtered data
                this._createGeoFormUI();
                // Create graphics layer to draw graphics
                this._graphicsLayer = new GraphicsLayer();
                this.map.addLayer(this._graphicsLayer);
                this.basemapExtent = this.appUtils.getBasemapExtent(this.baseMapLayers);
                // Create instance of Draw tool to draw the graphics on graphics layer
                this.toolbar = new Draw(this.map);
                // activate draw tool
                this._activateDrawTool();
                // Handle draw_activateDrawTool-end event which will be fired on selecting location
                on(this.toolbar, "draw-complete", lang.hitch(this, function (evt) {
                    // remove select location error message as location is selected now.
                    this._removeErrorNode(this.select_location.nextSibling);
                    // add drawn graphic on the graphics layer
                    this._addToGraphicsLayer(evt);
                    // resize map
                    this._resizeMap();
                    // Listen to draw complete event
                    this.onDrawComplete(evt);
                    var geometry;
                    if (evt.geometry.type === "point") {
                        geometry = evt.geometry;
                    } else {
                        geometry = this.firstMapClickPoint;
                    }
                    this.appUtils.getProjectedGeometry(geometry).then(
                            lang.hitch(this, function (returnedGeometry) {
                                this.appUtils.locatorInstance.locationToAddress(returnedGeometry, 100);
                                this.firstMapClickPoint = null;
                            }));
                }));
                // Handle click of Submit button
                on(this.submitButton, "click, keypress", lang.hitch(this, function (evt) {
                    if (!this.appUtils.validateEvent(evt)) {
                        return;
                    }
                    var commentSubmitStatus = this.appUtils.isCommentDateInRange(),
                        canSubmit = true;
                        if (commentSubmitStatus === null) {
                            if (this.appConfig.hasOwnProperty("reportingPeriod") &&
                                this.appConfig.reportingPeriod === "Closed") {
                                this.appUtils.reportingPeriodDialog.showDialog("reporting");
                                canSubmit = false;
                                return;
                            }
                        } else {
                            if (!commentSubmitStatus) {
                                canSubmit = false;
                                if (!this.appUtils.reportingPeriodDialog) {
                                    this.appUtils.createReportingPeriodDialog();
                                }
                                this.appUtils.reportingPeriodDialog.showDialog("reporting");
                                return;
                            }
                        }
                    if (canSubmit) {
                        this._submitForm();
                    }
                }));
                // Handle click of close button
                on(this.closeButton, "click, keypress", lang.hitch(this, this.closeForm));
                // Handle click of cancel button
                on(this.cancelButton, "click, keypress", lang.hitch(this, this._onCancelClick));
                on(this.cancelButton, "focusout", lang.hitch(this, function () {
                    this.closeButton.focus();
                }));
                // Initialize locator widget
                this.locator = new Locator({ "appUtils": this.appUtils, "isGeoformLocator": true, "map": this.map, "config": this.config, "appUtils": this.appUtils, "itemInfo": response.itemInfo.itemData, "layerId": this.layerId, "locatorContainer": this.geoformLocator, "handleFeatureSearch": false });
                // function call on selection of search result
                this.locator.onLocationCompleted = lang.hitch(this, this._validateAddress);
                //Listen for address list open/close event
                this.locator.onAddressListToggle = lang.hitch(this, function () {
                    //After list is populated with all the valid locations, resize the map because map changes its position
                    if (this.map) {
                        this.map.resize();
                    }
                });
                //Set placeholder text on load, if application is running in IE9
                if (has("ie") === 9) {
                    this.appUtils.displayPlaceHolderText(this.locator.txtSearch, response.itemInfo, this.config.i18n);
                }
                on(this.locator.txtSearch, "blur", lang.hitch(this, function () {
                    if (has("ie") === 9) {
                        this.appUtils.displayPlaceHolderText(this.locator.txtSearch, response.itemInfo, this.config.i18n);
                    }
                }));
                on(this.locator.txtSearch, "focus", lang.hitch(this, function () {
                    if (has("ie") === 9) {
                        this.appUtils.removePlaceHolderText(this.locator.txtSearch);
                    }
                }));
                on(this.locator.close, "click", lang.hitch(this, function () {
                    //Resize the map
                    this.map.resize();
                    if (has("ie") === 9) {
                        this.appUtils.displayPlaceHolderText(this.locator.txtSearch, response.itemInfo, this.config.i18n);
                    }
                }));
                // create geoLocation Button
                this.appUtils.createGeoLocationButton(response.itemInfo.itemData.baseMap.baseMapLayers, this.map, this.geoLocationButton, true);

                // function call on map resize
                on(window, 'resize', lang.hitch(this, function () {
                    this._resizeMap(true);
                }));
                // As map is in scrollable container if you scroll the container, map's position gets changed and map click will not give you proper screen point.
                // So to fix this, handle container's onscroll event and resize map so that map click will work properly.
                //Also hide the popups on scroll as geoform is in a container with absolute position so on scrolling geoform pop-ups will stick not stick to the input container
                on(dom.byId('geoFormBody'), 'scroll', lang.hitch(this, this._onGeoformScroll));

                on(window, "orientationchange", function () {
                    $(".selectDomain").blur();
                });

                //hide geoFormLoader div
                domClass.add(this.geoFormLoader, "esriCTHidden");

                // if changed extent not null
                if (this.changedExtent) {
                    this.map.setExtent(this.changedExtent);
                }

                //Populate location field after successfully fetching the address
                this.appUtils.onLocationToAddressComplete = lang.hitch(this, function (result) {
                    var addressString = "", key;
                    if (result.address && result.address.address &&
                            result.address.address.Match_addr) {
                        addressString = result.address.address.Match_addr;
                    } else {
                        addressString = "";
                        for (key in result.address.address) {
                        //Check if key exist and it is not equal to "Loc_name"
                            if (result.address.address.hasOwnProperty(key) && key !== "Loc_name") {
                                addressString += result.address.address[key] + " ";
                            }
                        }
                        addressString = lang.trim(addressString);
                    }
                    if (this.config.locationField !== "") {
                        this._populateLocationField(addressString);
                    }
                });

                //Reset the field in case of error
                this.appUtils.onLocationToAddressFailed = lang.hitch(this, function () {
                    if (this.config.locationField !== "") {
                        this._resetLocationField();
                    }
                });

                //Check if valid location field is configured
                if (this.config.locationField) {
                    this._findLocationField();
                }
            }));
        },

        _onCancelClick: function (evt) {
            if (!this.appUtils.validateEvent(evt)) {
                return;
            }
            if (domClass.contains(this.headerMessageDiv, "esriCTVisible")) {
                domClass.replace(this.headerMessageDiv, "esriCTHidden", "esriCTVisible");
            }
            this.toolbar.deactivate();
            this._clearSubmissionGraphic();
            this._resizedFiles = [];
            setTimeout(lang.hitch(this, function () {
                this.closeForm(evt);
                this._clearFormFields();
            }), 500);
        },

        /**
        * This function is called on Scroll.
        * @memberOf widgets/geo-form/geo-form
        */
        _onGeoformScroll: function () {
            //close the datePicker pop-ups
            $(".hasDatetimepicker").blur();
            $(".bootstrap-datetimepicker-widget").datetimepicker().hide();
            $(".popover.top.fade.show").each(function () {
                domClass.remove(this, "show");
            });
            //resize the map
            this._resizeMap();
        },

        /**
        * check if located address is in basemap extent
        * @param{object} geometry of located point on the map
        * @memberOf widgets/geo-form/geo-form
        */
        _validateAddress: function (geometry) {
            if (this.basemapExtent.contains(geometry)) {
                this._locateSelectedAddress(geometry);
                if (this.layer.geometryType === "esriGeometryPoint" &&
                        this.config.locationField !== "") {
                    this._populateLocationField(this.locator.txtSearch.value);
                }
                this.onLocationSelected(geometry);
            } else {
                this.appUtils.showError(this.appConfig.i18n.locator.locationOutOfExtent);
            }
        },

        /**
        * Locate address on map. If the layer is a point layer, a graphic will be added on map else it will zoom to the selected geometry
        * @param{object} geometry of located point on the map
        * @memberOf widgets/geo-form/geo-form
        */
        _locateSelectedAddress: function (geometry) {
            //check if the layer is a point layer
            if (this.layer.geometryType === "esriGeometryPoint") {
                this._addToGraphicsLayer(geometry);
                // Listen to draw complete event
                this.onDrawComplete(geometry);
            }
            // zoom to current location
            this._zoomToSelectedFeature(geometry);
        },

        /**
        * Filter operational layers. Display only selected layer on map and remove remaining layers from map
        * @param{array} opLayers, selected layer details for geo form
        * @memberOf widgets/geo-form/geo-form
        */
        _filterOperationalLayers: function (opLayers) {
            var i;
            for (i = 0; i < opLayers.length; i++) {
                // if layerId matches store it in this.layer
                // else remove that layer form map, so that only selected layer is visible on map.
                if (opLayers[i].id === this.layerId) {
                    this.layer = this.map.getLayer(opLayers[i].id);
                    this.setLocationPanelHint();
                    //Make sure we are not showing labels on geoform feature layer to make it consistent with main map
                    if (this.layer.showLabels) {
                        this.layer.showLabels = false;
                    }
                    if (this.isEdit && this.layer.hasAttachments) {
                        this._fetchExistingAttachment();
                    }
                } else {
                    if (this.appConfig.showNonEditableLayers) {
                        if (this.map.getLayer(opLayers[i].id)) {
                            if (opLayers[i].resourceInfo && opLayers[i].resourceInfo.capabilities) {
                                // condition to check if feature layer is non-editable and it is visible in TOC
                                if ((opLayers[i].resourceInfo.capabilities.indexOf("Create") === -1) &&
                                        ((opLayers[i].resourceInfo.capabilities.indexOf("Update") === -1) ||
                                        (opLayers[i].resourceInfo.capabilities.indexOf("Editing") === -1)) &&
                                        opLayers[i].visibility) {
                                    opLayers[i].layerObject.show(); // display non-editable layer
                                    // condition to check feature layer with create, edit, delete permissions and popup enabled, but all fields marked display only
                                } else if ((opLayers[i].resourceInfo.capabilities.indexOf("Create") !== -1) &&
                                        (opLayers[i].resourceInfo.capabilities.indexOf("Editing") !== -1) &&
                                        (opLayers[i].resourceInfo.capabilities.indexOf("Update") !== -1) &&
                                        (opLayers[i].popupInfo) &&
                                        this._checkDisplayPropertyOfFields(opLayers[i].popupInfo, opLayers[i].layerObject.fields) &&
                                        this.layerId !== opLayers[i].id) {
                                    opLayers[i].layerObject.show(); // display non-editable layer
                                    // condition to check feature layer with create, edit, delete permissions, but disabled on the layer in the map TOC
                                } else {
                                    opLayers[i].layerObject.hide();
                                }
                            }
                        }
                    } else {
                        if (opLayers[i].layerObject) {
                            opLayers[i].layerObject.hide();
                        } else if (opLayers[i].featureCollection) {
                            //Handle feature collection layers and show them on the map as non-editable layer
                            array.forEach(opLayers[i].featureCollection.layers, lang.hitch(this, function (featureCollectionLayer) {
                                if (featureCollectionLayer.layerObject && (featureCollectionLayer.layerObject.capabilities.indexOf("Create") === -1) &&
                                        ((featureCollectionLayer.layerObject.capabilities.indexOf("Editing") === -1) ||
                                        (featureCollectionLayer.layerObject.capabilities.indexOf("Update") === -1)) && opLayers[i].visibility) {
                                    featureCollectionLayer.layerObject.hide();
                                }
                            }));
                        }
                    }
                }
            }
        },

        /**
        * This function is used to check whether all fields are marked display or not
        * @memberOf widgets/webmap-list/webmap-list
        */
        _checkDisplayPropertyOfFields: function (popupInfo, fields) {
            var i, j;
            if (!popupInfo) {
                return false;
            }
            for (i = 0; i < popupInfo.fieldInfos.length; i++) {
                if (popupInfo.fieldInfos[i].isEditable) {
                    return false;
                }
            }
            // check if popup-info is available if not then return false
            if (popupInfo) {
                for (i = 0; i < popupInfo.fieldInfos.length; i++) {
                    for (j = 0; j < fields.length; j++) {
                        if (popupInfo.fieldInfos[i].fieldName === fields[j].name) {
                            // check if field is Editable
                            if (popupInfo.fieldInfos[i].visible) {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        },

        /**
        * Select fields from info popup
        * @param{object} Map response
        * @memberOf widgets/geo-form/geo-form
        */
        _filterLayerFields: function (response) {
            var layerFields = [], excludeDataTypes = [], layerField;
            // DataTypes to be excluded from Geoform
            excludeDataTypes = ["esriFieldTypeOID", "esriFieldTypeBlob", "esriFieldTypeRaster", "esriFieldTypeGUID", "esriFieldTypeGlobalID", "esriFieldTypeXML"];
            if (response.itemInfo.itemData.operationalLayers) {
                array.some(response.itemInfo.itemData.operationalLayers, lang.hitch(this, function (operationalLayer, index) {
                    // if layerId matches create filtered data array
                    if (operationalLayer.id === this.layerId) {
                        //To maintain the order of the fields form popup configuration first get all fields info in layerFields array
                        //then iterate through popupInfo and create fields to be shown in geoform.

                        // Create layerFields Key value pair according to fieldName
                        array.forEach(this.layer.fields, lang.hitch(this, function (layerField) {
                            layerFields[layerField.name] = layerField;
                        }));
                        // Iterate through all the fields in popup info,Merge field info from layer details and popup details and create sortedFields array.
                        array.forEach(response.itemInfo.itemData.operationalLayers[index].popupInfo.fieldInfos, lang.hitch(this, function (popupField) {
                            // If 'ReportedBy' field is present in the layer, set _layerHasReportedByField flag
                            if (popupField.fieldName === this.config.reportedByField) {
                                this._layerHasReportedByField = true;
                            }
                            layerField = layerFields[popupField.fieldName];
                            // check if layer is editable
                            if (popupField.isEditable && $.inArray(layerField.type, excludeDataTypes) === -1) {
                                layerField.alias = popupField.label;
                                layerField.editable = popupField.isEditable;
                                layerField.tooltip = popupField.tooltip;
                                layerField.stringFieldOption = popupField.stringFieldOption;
                                // if field has format
                                if (popupField.format) {
                                    layerField.format = popupField.format;
                                }
                                // if layer has type field set subTypes else set typeField as false
                                if (layerField.name === this.layer.typeIdField) {
                                    layerField.subTypes = this.layer.types;
                                    layerField.typeField = true;
                                } else {
                                    layerField.typeField = false;
                                }
                                this.sortedFields.push(layerField);
                            }
                        }));
                        return true;
                    }
                }));
            }
        },

        /**
        * Create UI for GeoForm
        * @memberOf widgets/geo-form/geo-form
        */
        _createGeoFormUI: function () {
            var geoformDetailsSectionLabel, geoformLocationSectionLabel, submitButtonText;
            domConstruct.empty(this.layerTitleDiv);
            // Set innerHTML for geo form header sections
            domAttr.set(this.layerTitleDiv, "innerHTML", this.layerTitle);
            //Show popup on click/hover of layer title div
            if (window.hasOwnProperty("ontouchstart") || window.ontouchstart !== undefined) {
                this._createTooltip(this.layerTitleDiv, this.layerTitle);
            }
            //Check for configurable parameter and accordingly set the section titles
            if (this.appConfig.geoformDetailsSectionLabel) {
                if (this.appConfig.geoformDetailsSectionLabel === "Details") {
                    geoformDetailsSectionLabel = this.appConfig.i18n.geoform.enterInformation;
                } else {
                    geoformDetailsSectionLabel = this.appConfig.geoformDetailsSectionLabel;
                }
            } else {
                geoformDetailsSectionLabel = this.appConfig.i18n.geoform.enterInformation;
            }

            if (this.appConfig.geoformLocationSectionLabel) {
                if (this.appConfig.geoformLocationSectionLabel === "Location") {
                    geoformLocationSectionLabel = this.appConfig.i18n.geoform.enterLocation;
                } else {
                    geoformLocationSectionLabel = this.appConfig.geoformLocationSectionLabel;
                }
            } else {
                geoformLocationSectionLabel = this.appConfig.i18n.geoform.enterLocation;
            }

            domAttr.set(this.enter_Information, "innerHTML", geoformDetailsSectionLabel);
            domAttr.set(this.enter_Information, "aria-label", geoformDetailsSectionLabel);
            domAttr.set(this.select_location, "innerHTML", geoformLocationSectionLabel);
            domAttr.set(this.select_location, "aria-label", geoformLocationSectionLabel);
            if (this.isEdit) {
                submitButtonText = this.appConfig.i18n.geoform.editReportButton;
            } else {
                submitButtonText = this.appConfig.i18n.geoform.reportItButton;
            }
            domAttr.set(this.submitButton, "innerHTML", submitButtonText);
            domAttr.set(this.cancelButton, "innerHTML", this.appConfig.i18n.geoform.cancelButton);
            // If sorted field array length is zero
            if (this.sortedFields.length === 0) {
                this._showErrorMessageDiv(this.appConfig.i18n.geoform.noFieldsConfiguredMessage, this.headerMessageDiv);
            }
            // Sort fields array by type
            this._sortedTypeFormElement();
            // Create attachments container, if layer supports attachments
            this._createAttachments();
            if (!this.isEdit) {
                //Whenever geoform is open set focus to geoform close button
                setTimeout(lang.hitch(this, function () {
                    this.closeButton.focus();
                }), 500);
            } else {
                this.enter_Information.focus();
            }
        },

        /**
        * Sort form elements by type
        * @memberOf widgets/geo-form/geo-form
        */
        _sortedTypeFormElement: function () {
            var hasDomainValue, hasDefaultValue;
            array.forEach(this.sortedFields, lang.hitch(this, function (currentField, index) {
                // Set true/false value to property 'isTypeDependent' of the field.
                currentField.isTypeDependent = false;
                array.forEach(this.layer.types, function (currentType) {
                    hasDomainValue = null;
                    hasDefaultValue = null;
                    hasDomainValue = currentType.domains[currentField.name];
                    hasDefaultValue = currentType.templates[0].prototype.attributes[currentField.name];
                    // If hasDefaultValue is 0 then we need to set isTypeDependent property to true
                    if (hasDefaultValue === 0) {
                        hasDefaultValue = true;
                    }
                    if ((hasDomainValue && hasDomainValue.type !== "inherited") || (hasDefaultValue && !currentField.typeField)) {
                        currentField.isTypeDependent = true;
                    }
                });
                // If isTypeDependent is true then it will return true
                if (currentField.isTypeDependent) {
                    return true;
                }
                // Create form elements (referenceNode is passed null)
                this._createFormElement(currentField, index, null);
            }));
        },

        /**
        * Destroy geo-form instance
        * @memberOf widgets/geo-form/geo-form
        */
        destroyInstance: function () {
            // check if map object is present, destroy map object and assign null value
            if (this.map) {
                this.map.destroy();
                this.map = null;
            }
            this.destroy();
        },

        /**
        * Clear previous attachment count and hide the message
        * @memberOf widgets/geo-form/geo-form
        */
        _clearAttachements: function () {
            var fileList, i;
            // Check for the file attachment container
            if (this.fileAttachmentList) {
                fileList = query(".alert-dismissible", this.fileAttachmentList);
                for (i = 0; i < fileList.length; i++) {
                    // Check for attachments file and replace the class
                    if (dom.byId(fileList[i].id.split("_")[0])) {
                        domClass.replace(dom.byId(fileList[i].id.split("_")[0]), "esriCTHideFileInputUI", "esriCTFileToSubmit");
                    }
                    // Destroy node by Id
                    if (dom.byId(fileList[i].id)) {
                        domConstruct.destroy(fileList[i].id);
                    }
                }
                // Update count of attachment
                this._updateAttachmentCount();
            }
        },

        /**
        * Create attachments container
        * @memberOf widgets/geo-form/geo-form
        */
        _createAttachments: function () {
            var fileInput, formContent, userFormNode, fileChange, fileAttachmentContainer, fileContainer, geoformAttachmentSectionLabel;
            // If layer has hasAttachments true
            if (this.layer.hasAttachments) {
                domConstruct.empty(this.attachmentSection);
                userFormNode = this.userForm;
                // Create container for hasAttachment
                formContent = domConstruct.create("div", {
                    "class": "form-group hasAttachment geoFormQuestionare esriCTGeoFormAttachmentLabel"
                }, this.attachmentSection);

                if (this.appConfig.geoformAttachmentSectionLabel) {
                    if (this.appConfig.geoformAttachmentSectionLabel === "Attachments") {
                        geoformAttachmentSectionLabel = this.appConfig.i18n.geoform.selectAttachments;
                    } else {
                        geoformAttachmentSectionLabel = this.appConfig.geoformAttachmentSectionLabel;
                    }
                } else {
                    geoformAttachmentSectionLabel = this.appConfig.i18n.geoform.selectAttachments;
                }
                // Select attachment label
                domConstruct.create("label", {
                    "innerHTML": geoformAttachmentSectionLabel,
                    "aria-label": geoformAttachmentSectionLabel,
                    "id": "geoFormAttachmentTitileLabel",
                    "class": "esriCTGeoFormTitles"
                }, formContent);
                domConstruct.create("br", {}, formContent);
                // Create div for Attachment button
                fileContainer = domConstruct.create("div", {
                    "class": "esriCTFileButtonContainer",
                    "title": this.appConfig.i18n.geoform.selectFileText
                }, formContent);
                this._fileInputIcon = domConstruct.create("button", {
                    "type": "button",
                    "tabindex": "0",
                    "aria-label": this.appConfig.i18n.geoform.selectFileText,
                    "innerHTML": this.appConfig.i18n.geoform.selectFileText,
                    "class": "fileInputButton btn  btn-default esriCTPointerCursor esriCTGeoFormButton esriCTApplicationColor esriCTButtonTextColor esriCTButtonBackgroundColor"
                }, fileContainer);
                // Show photo selected count
                domConstruct.create("div", {
                    "id": "attachmentSelectedCount",
                    "tabindex": "0",
                    "class": "esriCTHidden esriCTAttachmentSelectedCount"
                }, formContent);
                fileAttachmentContainer = domConstruct.create("div", {
                    "class": "container esriCTAttachmentContainer"
                }, formContent);
                this.fileAttachmentList = domConstruct.create("div", {
                    "class": "row esriCTFileAttachMentList"
                }, fileAttachmentContainer);
                // Create input container for attachments
                //Modified the accept param to allow user to upload videos with
                //ESRI supported format
                fileInput = domConstruct.create("input", {
                    "type": "file",
                    "accept": "image/*, video/*",
                    "aria-label": this.appConfig.i18n.geoform.selectFileText,
                    "name": "attachment",
                    "tabindex":"-1",
                    "style": { "height": dojo.coords(this._fileInputIcon).h + "px", "width": dojo.coords(this._fileInputIcon).w + "px" }
                }, domConstruct.create("form", { "tabindex": "-1", "id": "geoFormAttachment" + this._fileAttachmentCounter++, "class": "esriCTHideFileInputUI" }, fileContainer));
                domClass.add(fileInput, "esriCTPointerCursor");

                // Handle change event for file control
                on(this._fileInputIcon, "click, keypress", lang.hitch(this, function (evt) {
                    if (!this.appUtils.validateEvent(evt)) {
                        return;
                    }
                    fileInput.click();
                }));
                // Handle change event for file control
                fileChange = on(fileInput, "change", lang.hitch(this, function (evt) {
                    fileChange.remove();
                    this._onFileSelected(evt);
                }));
            }
        },

        /**
        * Show selected file on geoform and create new fileControl so that multiple files can be selected.
        * @param{object} evt - Event object which will be generated on file input change event.
        * @memberOf widgets/geo-form/geo-form
        */
        _onFileSelected: function (evt) {
            var newFormControl, fileInput, fileName, fileChange, alertHtml, target = evt.currentTarget || evt.srcElement;
            if (target && target.value) {
                fileName = target.value;
                fileName = fileName.split("\\")[fileName.split("\\").length - 1];
            } else {
                fileName = "";
            }
            //once file is selected change class so that the selected file will be added as attachment
            domClass.replace(target.parentNode, "esriCTFileToSubmit", "esriCTHideFileInputUI");
            domStyle.set(target.parentNode, "display", "none");
            //Add dismiss-able alert for each file, and show file name and file size in it.

            alertHtml = "<div id=" + target.parentNode.id + "_Close" + " class=\"esriCTFileAlert alert alert-success fade in alert-dismissible show\">";
            alertHtml += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\" tabindex=\"'0'\" aria-label=\"" +
            this.config.i18n.geoform.deleteAttachmentBtnText + "\">" + "X" + "</button>";
            alertHtml += "<span >" + fileName + "</span>";
            alertHtml += "</div>";
            alertHtml = domConstruct.place(alertHtml, this.fileAttachmentList, "last");
            //if file is removed then
            //replace the class from esriCTFileToSubmit to esriCTHideFileInputUI and update the file selected count
            $('#' + target.parentNode.id + "_Close").bind('closed.bs.alert', lang.hitch(this, function (evt) {
                domClass.replace(dom.byId(evt.target.id.split("_")[0]), "esriCTHideFileInputUI", "esriCTFileToSubmit");
                this._updateAttachmentCount();
                //Check if the resized image is being deleted by the user
                //if yes then delete the same image from the array
                var indexToMatch = domAttr.get(dom.byId(target.parentNode.id), "indexToMatch");
                if (indexToMatch) {
                    var indexToDelete = parseInt(indexToMatch, 10);
                    this._resizedFiles.splice(indexToDelete, 1, null);
                }
            }));

            //once filename is shown, update file attachments count
            this._updateAttachmentCount();
            //Check if file input container is present
            if ($(".hasAttachment")[0]) {
                newFormControl = domConstruct.create("form", { "id": "geoFormAttachment" + this._fileAttachmentCounter++, "class": "esriCTHideFileInputUI" }, $(".hasAttachment")[0]);
                //create new file input control so that multiple files can be attached
                fileInput = domConstruct.create("input", {
                    "type": "file",
                    "tabindex": "-1",
                    "accept": "image/*, video/*",
                    "name": "attachment",
                    "style": { "height": dojo.coords(this._fileInputIcon).h + "px", "width": dojo.coords(this._fileInputIcon).w + "px" }
                }, newFormControl);
                //place the newly created file-input control after file selection icon
                domConstruct.place(newFormControl, this._fileInputIcon, "after");
                //handle change event for file control if file size is
                fileChange = on(fileInput, "change", lang.hitch(this, function (evt) {
                    fileChange.remove();
                    this._onFileSelected(evt);
                }));
            }
            var _this = this;
            this.allMetaData = null;
            //Perform the geo tag functionality only when submitting new feature
            if (!this.isEdit) {
                //Check if attached image has a valid coordinates information
                EXIF.getData(evt.currentTarget.files[0], function () {
                    _this.allMetaData = EXIF.getAllTags(this);
                    if (_this.allMetaData && _this.allMetaData.GPSLatitude && _this.allMetaData.GPSLongitude) {
                        _this._locationDialog.showDialog("location");
                    }
                });
            }
            //If max size parameter exist and it is configured
            //then validate and reduce the image size
            if (this.config.hasOwnProperty("maxImageSize")
                && this.config.maxImageSize !== "actualSize") {
                this._validateAndReduceImageDimension(evt, target, _this, fileName);
            }
        },

        /**
        * validate and reduce the image size
        * @param{object} evt - Event object which will be generated on file input change event
        * @param{object} evt - Target node
        * @param{object} _this - Scope object
        * @param{string} fileName - File name of attachment
        * @memberOf widgets/geo-form/geo-form
        */
        _validateAndReduceImageDimension: function (evt, target, _this, fileName) {
            var reader;
            reader = new FileReader();
            reader.onload = function (readerEvent) {
                var image, formNode, formData, canvas;
                image = new Image();
                image.onload = function () {
                    // Resize the image
                    canvas = domConstruct.create('canvas'),
                        max_size = _this.config.maxImageSize,
                        width = image.width,
                        height = image.height;
                    if (max_size < height || max_size < width) {
                        if (width > height) {
                            if (width > max_size) {
                                height *= max_size / width;
                                width = max_size;
                            }
                        } else {
                            if (height > max_size) {
                                width *= max_size / height;
                                height = max_size;
                            }
                        }
                        canvas.width = width;
                        canvas.height = height;
                        canvas.getContext('2d').drawImage(image, 0, 0, width, height);
                        imageDataUrl = canvas.toDataURL('image/jpeg');
                        imageResized = _this.appUtils.dataURLToBlob(imageDataUrl);

                        formNode = domConstruct.create("form", { "enctype": "multipart/form-data" });
                        formData = new FormData(formNode);
                        formData.append("attachment", imageResized, fileName);
                        _this._resizedFiles.push(formData);
                        //add attribute, this will be useful while accessing the image
                        domAttr.set(dom.byId(target.parentNode.id), "indexToMatch",
                            _this._resizedFiles.length - 1);
                        domClass.remove(target.parentNode, "esriCTFileToSubmit");
                    }
                };
                image.src = readerEvent.target.result;
                evt.target.files[0] = readerEvent.target.result;
            };
            reader.readAsDataURL(evt.currentTarget.files[0]);
        },

        /**
        * This function will be used to zoom to the attached image's location
        * @memberOf widgets/geo-form/geo-form
        */
        _gotoImageLocation: function (allMetaData) {
            var lat = allMetaData.GPSLatitude;
            var lon = allMetaData.GPSLongitude;
            //Convert coordinates to WGS84 decimal
            var latRef = allMetaData.GPSLatitudeRef || "N";
            var lonRef = allMetaData.GPSLongitudeRef || "W";
            lat = (lat[0] + lat[1] / 60 + lat[2] / 3600) * (latRef == "N" ? 1 : -1);
            lon = (lon[0] + lon[1] / 60 + lon[2] / 3600) * (lonRef == "W" ? -1 : 1);
            //alert("lat : " + lat + " long : " + lon);
            var point = new Point(lon, lat);
            if (this._selectLayerType() !== "point") {
                this.onLocationSelected(point);
                this._locationDialog.hideDialog("location");
                return;
            }
            // remove select location error message as location is selected now.
            this._removeErrorNode(this.select_location.nextSibling);
            // add drawn graphic on the graphics layer
            this._addToGraphicsLayer(point, false);
            // resize map
            this._resizeMap();
            // Listen to draw complete event
            this.onDrawComplete(point);
            this.appUtils.locatorInstance.locationToAddress(webMercatorUtils.webMercatorToGeographic(point), 100);
            this.firstMapClickPoint = null;
            // zoom to current location
            this._zoomToSelectedFeature(point);
            this.onLocationSelected(point);
            this._locationDialog.hideDialog("location");
            setTimeout(lang.hitch(this, function () {
                query(".fileInputButton", this.domNode)[0].focus();
            }), 1000);
        },

        /**
        * This function will update attachment countand based on count will show/hide message in GeoForm.
        * @memberOf widgets/geo-form/geo-form
        */
        _updateAttachmentCount: function () {
            var photoSelectedDiv = dom.byId("attachmentSelectedCount"), selectedAttachmentsCount;
            if (photoSelectedDiv) {
                selectedAttachmentsCount = query(".alert-dismissible", this.fileAttachmentList).length;
                if (selectedAttachmentsCount > 0) {
                    domClass.remove(photoSelectedDiv, "esriCTHidden");
                    domAttr.set(photoSelectedDiv, "innerHTML", selectedAttachmentsCount + " " + this.appConfig.i18n.geoform.attachmentSelectedMsg);
                } else {
                    domAttr.set(photoSelectedDiv, "innerHTML", "");
                    domClass.add(photoSelectedDiv, "esriCTHidden");
                }
            }
            this._resizeMap();
        },

        /**
        * Create form elements
        * @param{object} currentField, object of current field in the info pop
        * @param{int} index, index of current field in the array
        * @param{object} referenceNode, Parent Node for dependent field
        * @memberOf widgets/geo-form/geo-form
        */
        _createFormElement: function (currentField, index, referenceNode, subFieldValues) {
            var fieldname, labelContent, fieldAttribute, fieldLabelText, formContent, requireField, userFormNode;
            userFormNode = this.userForm;
            //code to put asterisk mark for mandatory fields and also to give it a mandatory class.
            formContent = domConstruct.create("div", {}, userFormNode);
            // If dependent field has Reference Node
            if (referenceNode) {
                domConstruct.place(formContent, referenceNode, "after");
                domClass.add(formContent, "fade");
                setTimeout(function () {
                    domClass.add(formContent, "show");
                }, 100);
            }
            // If fields are not nullable set to mandatory fields
            if (!currentField.nullable || currentField.typeField) {
                domClass.add(formContent, "form-group geoFormQuestionare mandatory");
                requireField = domConstruct.create("small", {
                    className: 'esriCTRequireFieldStyle',
                    innerHTML: this.appConfig.i18n.geoform.requiredField
                }, formContent);
            } else {
                domClass.add(formContent, "form-group geoFormQuestionare");
            }
            // If field has alias
            // else Set field name
            if (currentField.alias) {
                fieldLabelText = currentField.alias;
            } else {
                fieldLabelText = currentField.name;
            }
            // assign field name
            fieldname = currentField.name;
            // Create Label
            labelContent = domConstruct.create("label", {
                "for": fieldname,
                className: "control-label",
                innerHTML: fieldLabelText,
                id: fieldname + "_label_" + index
            }, formContent);
            // Set required field with label
            if (requireField && labelContent) {
                domConstruct.place(requireField, labelContent, "last");
            }
            // set default Values to the fields
            if (this.layer.templates[0] && !currentField.defaultValue) {
                for (fieldAttribute in this.layer.templates[0].prototype.attributes) {
                    if (this.layer.templates[0].prototype.attributes.hasOwnProperty(fieldAttribute)) {
                        if (fieldAttribute.toLowerCase() === fieldname.toLowerCase()) {
                            if (this.layer.templates[0].prototype.attributes[fieldAttribute] !== null && lang.trim(this.layer.templates[0].prototype.attributes[fieldAttribute].toString()) !== "") {
                                currentField.defaultValue = this.layer.templates[0].prototype.attributes[fieldAttribute];
                            }
                        }
                    }
                }
            }
            //if loggedInUser info is available and fields are configured then populate it with the user info
            if (this.loggedInUser !== null) {
                if (this.config.firstNameField && this.config.firstNameField !== "" && currentField.type === "esriFieldTypeString" &&
                    fieldname.toLowerCase() === this.config.firstNameField.toLowerCase()) {
                    if (this.loggedInUser.firstName.length > currentField.length) {
                        currentField.defaultValue = this.loggedInUser.firstName.slice(0, currentField.length);
                    } else {
                        currentField.defaultValue = this.loggedInUser.firstName;
                    }
                }
                if (this.config.lastNameField && this.config.lastNameField !== "" && currentField.type === "esriFieldTypeString" &&
                    fieldname.toLowerCase() === this.config.lastNameField.toLowerCase()) {
                    if (this.loggedInUser.lastName.length > currentField.length) {
                        currentField.defaultValue = this.loggedInUser.lastName.slice(0, currentField.length);
                    } else {
                        currentField.defaultValue = this.loggedInUser.lastName;
                    }
                }
                if (this.config.emailField && this.config.emailField !== "" && currentField.type === "esriFieldTypeString" &&
                    fieldname.toLowerCase() === this.config.emailField.toLowerCase()) {
                    if (this.loggedInUser.email.length > currentField.length) {
                        currentField.defaultValue = this.loggedInUser.email.slice(0, currentField.length);
                    } else {
                        currentField.defaultValue = this.loggedInUser.email;
                    }
                }
            }
            // Set hint text for range domain Value
            this._createRangeText(currentField, formContent, fieldname);
            // If field is defined for custom cascading select then override the domain/normal field input and show dropdown with values defined in CSV
            // For cascading select skip fields with date type
            // If field has coded domain value and typeField set to true then create form elements for domain fields
            // else create form elements for non domain fields
            if (currentField.type !== "esriFieldTypeDate" && this.customCodedValues &&
                ((this.customCodedValues[fieldname] && this.customCodedValues[fieldname].codedValues && this.customCodedValues[fieldname].codedValues.length > 0) ||
                    subFieldValues)) {
                this._createCustomCodedValueFormElements(currentField, formContent, fieldname, subFieldValues);
            } else if (currentField.domain || currentField.typeField) {
                this._createDomainValueFormElements(currentField, formContent, fieldname);
            } else {
                this._createInputFormElements(currentField, formContent, fieldname);
            }
            //hide Loading Indicator
            this.appUtils.hideLoadingIndicator();
            // Resize Map
            this._resizeMap();
        },

        /**
        * Create range help text for elements.
        * @param{object} currentField, object of current field in the info pop
        * @param{object} formContent, Parent Node of the field inside geo form
        * @param{string} fieldname, name of the field
        * @memberOf widgets/geo-form/geo-form
        */
        _createRangeText: function (currentField, formContent, fieldname) {
            var options = {};
            // if field is required and field exists then set required field as a true
            if (!currentField.nullable && this.inputContent) {
                this.inputContent.setAttribute("aria-required", true);
                this.inputContent.setAttribute("required", "");
            }
            // if info pop has tooltip then create info popup hint text
            if (currentField.tooltip) {
                domConstruct.create("p", {
                    className: "help-block esriCTBodyTextColor esriCTHintStyle",
                    innerHTML: currentField.tooltip
                }, formContent);
            }
            // If field has range domain help text
            if (this.rangeHelpText) {
                options = {
                    trigger: 'focus',
                    placement: 'top',
                    container: 'body',
                    content: this.rangeHelpText,
                    html: true
                };
                $('#' + fieldname).popover(options);
                this.rangeHelpText = null;
            }
        },

        /**
        * Create Domain coded value elements of form.
        * @param{object} currentField, object of current field in the info pop
        * @param{object} formContent, Parent Node of the field inside geo form
        * @param{string} fieldname, name of the field
        * @memberOf widgets/geo-form/geo-form
        */
        _createDomainValueFormElements: function (currentField, formContent, fieldname) {
            var date, inputRangeDateGroupContainer, rangeDefaultDate, currentSelectedDate, formatedDate;
            if (this.isEdit) {
                //get field value
                currentField.defaultValue = this.item.attributes[fieldname];
            }
            if ((currentField.domain && (currentField.domain.type === 'undefined' || currentField.domain.type === undefined || currentField.domain.type === 'codedValue')) || currentField.typeField) {
                this._createCodedValueFormElements(currentField, formContent, fieldname);
            } else {
                // if field type is date create date field
                if (currentField.type === "esriFieldTypeDate") {
                    // create notation Icon for date field
                    inputRangeDateGroupContainer = this._addNotationIcon(formContent, "glyphicon-calendar");
                    // create date field
                    this.inputContent = this._createDateField(inputRangeDateGroupContainer, true, fieldname, currentField);
                    if (currentField.defaultValue) {
                        date = new Date(currentField.defaultValue);
                        // set format to the current date
                        rangeDefaultDate = moment(new Date(date)).format($(inputRangeDateGroupContainer).data("DateTimePicker").format());
                        // set current date to date field
                        $(inputRangeDateGroupContainer).data("DateTimePicker").date(rangeDefaultDate);
                        if (!this.isEdit) {
                            // set default value and id to the array
                            this.defaultValueArray.push({ defaultValue: currentField.defaultValue, id: this.inputContent.id, type: currentField.type });
                        }
                    } else {
                        ////Check if todays date falls between minimum and maximum date
                        if (currentField.domain.maxValue > Date.now() && currentField.domain.minValue < Date.now()) {
                            currentSelectedDate = Date.now();
                            $(inputRangeDateGroupContainer).data("DateTimePicker").date(moment(Date.now()).format($(inputRangeDateGroupContainer).data("DateTimePicker").format()));
                        } else {
                            currentSelectedDate = currentField.domain.minValue;
                        }
                        formatedDate = moment(new Date(currentSelectedDate)).format($(inputRangeDateGroupContainer).data("DateTimePicker").format());
                        $(inputRangeDateGroupContainer).data("DateTimePicker").date(formatedDate);
                        if (!this.isEdit) {
                            this.defaultValueArray.push({ defaultValue: currentSelectedDate, id: this.inputContent.id, type: currentField.type });
                        }
                    }
                    // Assign value to the range help text
                    this.rangeHelpText = string.substitute(this.appConfig.i18n.geoform.dateRangeHintMessage, {
                        minValue: moment(new Date(currentField.domain.minValue)).format($(inputRangeDateGroupContainer).data("DateTimePicker").format()),
                        maxValue: moment(new Date(currentField.domain.maxValue)).format($(inputRangeDateGroupContainer).data("DateTimePicker").format()),
                        openStrong: "<strong>",
                        closeStrong: "</strong>"
                    });
                } else {
                    // if field type is integer
                    this.rangeHelpText = this._setRangeForm(currentField, formContent, fieldname);
                }
            }
        },

        /**
        * Create coded value elements of form
        * @param{object} currentField, object of current field in the info pop
        * @param{object} formContent, Parent Node of the field inside geo form
        * @param{string} fieldname, name of the field
        * @memberOf widgets/geo-form/geo-form
        */
        _createCodedValueFormElements: function (currentField, formContent, fieldname) {
            var selectOptions;
            // check for fieldType: if not present create dropdown
            // If present check for fieldType value and accordingly populate the control
            // create controls for select
            this.inputContent = domConstruct.create("select", {
                className: "form-control selectDomain custom-select",
                "id": fieldname
            }, formContent);
            selectOptions = domConstruct.create("option", {
                innerHTML: this.appConfig.i18n.geoform.selectDefaultText,
                value: ""
            }, this.inputContent);
            // On selection Change
            this._codedValueOnChange(currentField);
            // check for domain value and create control for drop down list
            if (currentField.domain && !currentField.typeField) {
                array.forEach(currentField.domain.codedValues, lang.hitch(this, function (currentOption) {
                    selectOptions = domConstruct.create("option", {
                        innerHTML: currentOption.name,
                        value: currentOption.code
                    }, this.inputContent);
                    // if field contain default value, make that option selected
                    if (currentField.defaultValue !== undefined && currentField.defaultValue !== null && currentField.defaultValue !== "" && currentField.defaultValue.toString() === currentOption.code.toString()) {
                        // set selected is true
                        domAttr.set(selectOptions, "selected", true);
                        domAttr.set(selectOptions, "defaultSelected", true);
                        domClass.add(this.inputContent.parentNode.lastChild, "is-valid");
                        if (!this.isEdit) {
                            // set default value and id into the array
                            this.defaultValueArray.push({ defaultValue: currentField.defaultValue, id: this.inputContent.id });
                        }
                    }
                }));
            } else {
                // default values for subtypes(if any) has to be handled here
                array.forEach(currentField.subTypes, lang.hitch(this, function (currentOption) {
                    selectOptions = domConstruct.create("option", {}, this.inputContent);
                    selectOptions.text = currentOption.name;
                    selectOptions.value = currentOption.id;
                    // if field contain default value, make that option selected
                    if (this.item && this.item.attributes[fieldname] !== undefined && this.item.attributes[fieldname] !== null && this.item.attributes[fieldname] !== "" && this.item.attributes[fieldname].toString() === currentOption.id.toString()) {
                        domAttr.set(this.inputContent, "value", currentOption.id);
                        domClass.add(this.inputContent.parentNode.lastChild, "is-valid");
                    }
                }));
                if (currentField.typeField) {
                    this._validateTypeFields({ 'currentTarget': this.inputContent }, currentField);
                }
            }
        },

        _createCustomCodedValueFormElements: function (currentField, formContent, fieldname, subFieldValues) {
            var selectOptions;
            if (this.isEdit) {
                //get field value
                currentField.defaultValue = this.item.attributes[fieldname];
            }
            // check for fieldType: if not present create dropdown
            // If present check for fieldType value and accordingly populate the control
            // create controls for select
            this.inputContent = domConstruct.create("select", {
                className: "form-control selectDomain custom-select",
                "id": fieldname
            }, formContent);
            selectOptions = domConstruct.create("option", {
                innerHTML: this.appConfig.i18n.geoform.selectDefaultText,
                value: ""
            }, this.inputContent);

            if (subFieldValues) {
                optionsInfo = subFieldValues;
                // On selection Change
                this._codedValueOnChange(currentField);
            } else {
                optionsInfo = this.customCodedValues[fieldname];
                // On selection Change
                this._customCodedValueOnChange(currentField);
            }
            // check for domain value and create control for drop down list
            if (optionsInfo.codedValues) {
                array.forEach(optionsInfo.codedValues, lang.hitch(this, function (currentOption) {
                    selectOptions = domConstruct.create("option", {
                        innerHTML: currentOption.name,
                        value: currentOption.code
                    }, this.inputContent);
                    // if field contain default value, make that option selected
                    if (currentField.defaultValue !== undefined && currentField.defaultValue !== null && currentField.defaultValue !== "" && currentField.defaultValue.toString() === currentOption.code.toString()) {
                        // set selected is true
                        domAttr.set(selectOptions, "selected", true);
                        domAttr.set(selectOptions, "defaultSelected", true);
                        domClass.add(this.inputContent.parentNode.lastChild, "is-valid");
                        if (!this.isEdit) {
                            // set default value and id into the array
                            this.defaultValueArray.push({ defaultValue: currentField.defaultValue, id: this.inputContent.id });
                        }
                    }
                }));
            }
        },

        /**
        * Take appropriate actions on selection of a subtype
        * @param{object} currentField, object of current field in the info pop
        * @memberOf widgets/geo-form/geo-form
        */
        _codedValueOnChange: function (currentField) {
            // event on change
            on(this.inputContent, "change", lang.hitch(this, function (evt) {
                // function call to take appropriate actions on selection of a subtype
                if (currentField.typeField) {
                    this._validateTypeFields(evt, currentField);
                }
                // To apply is-valid class on selection of a valid option
                // else remove is-valid class
                if (evt.target.value !== "") {
                    var targetNode = evt.currentTarget || evt.srcElement;
                    if (query(".errorMessage", targetNode.parentNode).length !== 0) {
                        domConstruct.destroy(query(".errorMessage", targetNode.parentNode)[0]);
                        domClass.remove(evt.target.parentNode.lastChild, "is-invalid");
                    }
                    domClass.add($(evt.target.parentNode.lastChild)[0], "is-valid");
                } else {
                    domClass.remove($(evt.target.parentNode.lastChild)[0], "is-valid");
                }
            }));
        },

        _getSubFieldsValues: function (currentField, currentSelectedValue) {
            var subFields = [];
            if (currentField.type !== "esriFieldTypeDate") {
                array.some(this.customCodedValues[currentField.name].codedValues, lang.hitch(this, function (eachCodedValue) {
                    if (eachCodedValue.code === currentSelectedValue && eachCodedValue.subDomains) {
                        subFields = lang.clone(eachCodedValue.subDomains);
                        return true;
                    }
                }));
            }
            return subFields;
        },

        _customCodedValueOnChange: function (currentField) {
            // event on change
            on(this.inputContent, "change", lang.hitch(this, function (evt) {
                var currentSelectedValue = evt.currentTarget.value;
                var reconstructField = [];
                var allSubDomains = this._getSubFieldsValues(currentField, currentSelectedValue);
                //Reconstruct the field in case empty is selected or current custom domain dont have any subdomain values configured
                if (currentSelectedValue === "" || !allSubDomains ||allSubDomains.length === 0) {
                    if (currentField && currentField.name && this.customCodedValues[currentField.name]) {
                        array.forEach(this.customCodedValues[currentField.name].codedValues, lang.hitch(this, function (eachCodedValue) {
                            var allSubDomainFields = [];
                            if(eachCodedValue && eachCodedValue.subDomains){
                                allSubDomainFields = Object.keys(eachCodedValue.subDomains);
                            }
                            if (eachCodedValue.subDomains && allSubDomainFields.length > 0) {
                                for (var subFieldIndex in allSubDomainFields) {
                                    var field = allSubDomainFields[subFieldIndex];
                                    if (field && dom.byId(field) && reconstructField.indexOf(field) < 0) {
                                        reconstructField.push(field);
                                    }
                                }

                            }
                        }));
                        if (reconstructField.length > 0) {
                            array.forEach(this.sortedFields, lang.hitch(this, function (field, index) {
                                if (reconstructField.indexOf(field.name) !== -1) {
                                    var refNode = null;
                                    if (dom.byId(field.name)) {
                                        refNode = dom.byId(field.name).parentNode;
                                        domConstruct.destroy(dom.byId(field.name));
                                        domConstruct.empty(refNode);
                                        domClass.remove(refNode);
                                    }
                                    // create form elements for dependent values
                                    this._createFormElement(field, index, refNode);
                                }

                            }));
                        }
                        // resize map
                        this._resizeMap();
                    }
                } else {
                    array.forEach(this.sortedFields, lang.hitch(this, function (field, index) {
                        if (allSubDomains && allSubDomains.hasOwnProperty(field.name) && field.type !== "esriFieldTypeDate") {
                            var refNode = null;
                            if (dom.byId(field.name)) {
                                refNode = dom.byId(field.name).parentNode;
                                domConstruct.destroy(dom.byId(field.name));
                                domConstruct.empty(refNode);
                                domClass.remove(refNode);
                            }
                            // create form elements for dependent values
                            this._createFormElement(field, index, refNode, allSubDomains[field.name]);
                        }
                    }));
                }
                // To apply is-valid class on selection of a valid option
                // else remove is-valid class
                if (evt.target.value !== "") {
                    var targetNode = evt.currentTarget || evt.srcElement;
                    if (query(".errorMessage", targetNode.parentNode).length !== 0) {
                        domConstruct.destroy(query(".errorMessage", targetNode.parentNode)[0]);
                        domClass.remove(evt.target.parentNode.lastChild, "is-invalid");
                    }
                    domClass.add($(evt.target.parentNode.lastChild)[0], "is-valid");
                } else {
                    domClass.remove($(evt.target.parentNode.lastChild)[0], "is-valid");
                }
            }));
        },

        /**
        * Create input elements of form.
        * @param{object} currentField, object of current field in the info pop
        * @param{object} formContent, Parent Node of the field inside geo form
        * @param{string} fieldname, name of the field
        * @memberOf widgets/geo-form/geo-form
        */
        _createInputFormElements: function (currentField, formContent, fieldname) {
            var inputDateGroupContainer;
            // Create field controls on basis of their type
            switch (currentField.type) {
            case "esriFieldTypeString":
                if (currentField.stringFieldOption === "textarea") {
                    this.inputContent = domConstruct.create("textarea", {
                        className: "form-control",
                        "data-input-type": "String",
                        "rows": 4,
                        "maxLength": currentField.length,
                        "id": fieldname
                    }, formContent);
                } else {
                    this.inputContent = domConstruct.create("input", {
                        type: "text",
                        className: "form-control",
                        "data-input-type": "String",
                        "maxLength": currentField.length,
                        "id": fieldname
                    }, formContent);
                }
                break;
            case "esriFieldTypeSmallInteger":
                this.inputContent = domConstruct.create("input", {
                    type: "text",
                    className: "form-control",
                    "data-input-type": "SmallInteger",
                    "id": fieldname,
                    "pattern": "[0-9]*"
                }, formContent);
                break;
            case "esriFieldTypeInteger":
                this.inputContent = domConstruct.create("input", {
                    type: "text",
                    className: "form-control",
                    "data-input-type": "Integer",
                    "id": fieldname,
                    "pattern": "[0-9]*"
                }, formContent);
                break;
            case "esriFieldTypeSingle":
                this.inputContent = domConstruct.create("input", {
                    type: "text",
                    className: "form-control",
                    "data-input-type": "Single",
                    "id": fieldname
                }, formContent);
                break;
            case "esriFieldTypeDouble":
                this.inputContent = domConstruct.create("input", {
                    type: "text",
                    className: "form-control",
                    "data-input-type": "Double",
                    "id": fieldname,
                    step: ".1"
                }, formContent);
                break;
            case "esriFieldTypeDate":
                // add notation icon for calendar
                inputDateGroupContainer = this._addNotationIcon(formContent, "glyphicon-calendar");
                this.inputContent = this._createDateField(inputDateGroupContainer, false, fieldname, currentField);
                break;
            }
            // add default values to the fields
            this._addInputElementsValue(currentField, formContent, inputDateGroupContainer);
        },

        /**
        * Add default values to the fields
        * @param{object} currentField, object of current field in the info pop
        * @param{object} formContent, Parent Node of the field inside geo form
        * @param{object} inputDateGroupContainer, container for the date time picker
        * @memberOf widgets/geo-form/geo-form
        */
        _addInputElementsValue: function (currentField, formContent, inputDateGroupContainer) {
            var defaultDate, date;
            if (this.isEdit) {
                //get default field value if t is not exist in feature attributes
                currentField.defaultValue = this.item.attributes[this.inputContent.id];
            }
            // If default values is present assign it to the field
            if (currentField.defaultValue) {
                // If field type is date assign date to date time picker
                // else assign default value to the input controls
                if (currentField.type === "esriFieldTypeDate") {
                    date = new Date(currentField.defaultValue);
                    // set current date to date field
                    $(inputDateGroupContainer).data("DateTimePicker").date(date);
                   
                    if (!this.isEdit) {
                        this.defaultValueArray.push({ defaultValue: currentField.defaultValue, id: this.inputContent.id, type: currentField.type });
                    }
                } else {
                    domAttr.set(this.inputContent, "value", currentField.defaultValue);
                    domClass.add(formContent.lastChild, "is-valid");
                    if (!this.isEdit) {
                        this.defaultValueArray.push({ defaultValue: currentField.defaultValue, id: this.inputContent.id });
                    }
                }
            } else {
                // else assign current date to the date time picker
                if (currentField.type === "esriFieldTypeDate") {
                    // set current date to date field
                    $(inputDateGroupContainer).data("DateTimePicker").date(new Date());
                    // set format to the current date
                    defaultDate = moment(new Date()).format($(inputDateGroupContainer).data("DateTimePicker").format());
                    if (!this.isEdit) {
                        this.defaultValueArray.push({ defaultValue: new Date(), id: this.inputContent.id, type: currentField.type });
                    }
                }
            }
            // If field type is not date, validate fields on focus out
            if (currentField.type !== "esriFieldTypeDate") {
                // Set Validation for the field on focus out
                on(this.inputContent, "focusout", lang.hitch(this, function (evt) {
                    this._validateField(evt, currentField, true);
                }));
            }
        },

        /**
        * Validate date range field
        * @param{object} currentField, object of current field in the info pop
        * @param{object} formContent, Parent Node of the field inside geo form
        * @param{string} fieldname, name of the field
        * @memberOf widgets/geo-form/geo-form
        */
        _setRangeForm: function (currentField, formContent, fieldname) {
            var setStep, setDefault = "",
                stepDivisibility = 'none',
                decimalPoints = 0,
                inputcontentSpinner, rangeHelpText;
            // create container for range text and assign minimum and maximum values
            this.inputContent = domConstruct.create("input", {
                id: fieldname,
                type: "text",
                className: "form-control form-control-lg",
                min: currentField.domain.minValue.toString(),
                max: currentField.domain.maxValue.toString()
            }, formContent);
            domAttr.set(this.inputContent, "data-input-type", currentField.type.replace("esriFieldType", ""));
            // Check if default Value is present
            if (currentField.defaultValue) {
                setDefault = currentField.defaultValue;
                domClass.add(this.inputContent.parentNode.lastChild, "is-valid");
                if (!this.isEdit) {
                    this.defaultValueArray.push({ defaultValue: setDefault, id: this.inputContent.id, type: "range" });
                }
            }
            // Set minimum and maximum value in range domain
            if (domAttr.get(this.inputContent, "data-input-type") === "Double" || domAttr.get(this.inputContent, "data-input-type") === "Single") {
                decimalPoints = 2;
                if (currentField.domain.minValue - Math.floor(currentField.domain.minValue) === 0.5) {
                    setStep = 0.5;
                } else {
                    setStep = 0.1;
                }
            } else {
                setStep = 1;
                stepDivisibility = 'round';
            }
            // Set Touch Spinner for domain coded numeric values
            inputcontentSpinner = $(this.inputContent).TouchSpin({
                initval: setDefault,
                min: currentField.domain.minValue.toString(),
                max: currentField.domain.maxValue.toString(),
                forcestepdivisibility: stepDivisibility,
                step: setStep,
                boostat: 5,
                decimals: decimalPoints,
                maxboostedstep: 10
            });
            // Touch Spinner on keyup event
            this._inputTouchspinOnKeyup(inputcontentSpinner, currentField, this.inputContent);
            // Set minimum and maximum value to the rangeHelpText
            rangeHelpText = string.substitute(this.appConfig.i18n.geoform.numericRangeHintMessage, {
                minValue: currentField.domain.minValue.toString(),
                maxValue: currentField.domain.maxValue.toString(),
                openStrong: "<strong>",
                closeStrong: "</strong>"
            });
            // return value
            return rangeHelpText;
        },

        /**
        * Event to address validations for manual entry in the touch-spinner input
        * @param{object} inputcontentSpinner, container of TouchSpin
        * @param{object} currentField, object of current field in the info pop
        * @memberOf widgets/geo-form/geo-form
        */
        _inputTouchspinOnKeyup: function (inputcontentSpinner, currentField, inputContent) {
            // Touch Spinner on keyup event
            on(inputContent, "keyup", lang.hitch(this, function () {
                // replace classes on key up event
                if (inputContent.value === "") {
                    domClass.remove(inputContent.parentNode.childNodes[2], "is-valid");
                } else {
                    domClass.add(inputContent.parentNode.childNodes[2], "is-valid");
                }
            }));
            // Touch Spinner event
            on(inputcontentSpinner, "touchspin.on.startspin", lang.hitch(this, function (evt) {
                inputcontentSpinner.trigger("touchspin.updatesettings", {});
                var targetNode = evt.currentTarget || evt.srcElement;
                domClass.add(targetNode.parentNode.parentNode.childNodes[1].childNodes[2], "is-valid");
            }));
            // if not nullable field
            if (!currentField.nullable) {
                this.inputContent.setAttribute("aria-required", true);
                this.inputContent.setAttribute("required", "");
            }
        },

        /**
        * Validate fields defined within subtypes
        * @param{object} currentTarget, on change event current target field
        * @param{object} currentField, object of current field in the info pop
        * @memberOf widgets/geo-form/geo-form
        */
        _validateTypeFields: function (evt, currentField) {
            var selectedType, defaultValue, referenceNode, currentTarget = evt.currentTarget || evt.srcElement;
            // Validation for empty field
            // if field value is empty reset subtypes field
            if (currentTarget.value === "") {
                // if no type is selected, remove type dependent fields
                array.forEach(this.sortedFields, lang.hitch(this, function (currentInput) {
                    if (!currentInput.isTypeDependent) {
                        return true;
                    }
                    // rest form field and show dependent field in the form
                    this._resetSubTypeFields(currentInput);
                    //resize map
                    this._resizeMap();
                }));
            } else {
                // get all the domains and default values of the selected subtype
                array.some(currentField.subTypes, function (currentSelection) {
                    if (currentTarget.value === currentSelection.id.toString()) {
                        selectedType = currentSelection;
                        return true;
                    }
                });

                // initial point of reference to put elements
                referenceNode = dom.byId(this.layer.typeIdField).parentNode;
                // code to populate type dependent fields
                array.forEach(this.sortedFields, lang.hitch(this, function (currentInput, index) {
                    var field = null, fieldAttribute, hasDomainValue, hasDefaultValue;
                    hasDomainValue = selectedType.domains[currentInput.name];
                    hasDefaultValue = selectedType.templates[0].prototype.attributes[currentInput.name];
                    if ((hasDomainValue && hasDomainValue.type !== "inherited") || (hasDefaultValue && !currentInput.typeField) || (hasDefaultValue === 0 && !currentInput.typeField)) {
                        currentInput.isTypeDependent = true;
                    }
                    // condition to filter out fields independent of subtypes
                    if (!currentInput.isTypeDependent) {
                        return true;
                    }
                    // mixin array of sorted field and info pop field
                    array.some(this.layer.fields, function (layerField) {
                        if (layerField.name === currentInput.name) {
                            field = lang.clone(lang.mixin(layerField, currentInput));
                            return true;
                        }
                    });
                    // fetch the default value of a field for selected subtype.
                    if (selectedType.templates[0]) {
                        for (fieldAttribute in selectedType.templates[0].prototype.attributes) {
                            if (selectedType.templates[0].prototype.attributes.hasOwnProperty(fieldAttribute)) {
                                if (fieldAttribute.toLowerCase() === field.name.toLowerCase()) {
                                    if (selectedType.templates[0].prototype.attributes[fieldAttribute] !== null && lang.trim(selectedType.templates[0].prototype.attributes[fieldAttribute].toString()) !== "") {
                                        defaultValue = selectedType.templates[0].prototype.attributes[fieldAttribute];
                                        field.defaultValue = defaultValue;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    // validate dependent values
                    this._validateTypeFieldsValue(selectedType, field, referenceNode, index);
                }));
                // resize map
                this._resizeMap();
            }
        },

        /**
        * Populate domain properties for current field in form elements
        * @param{string} selectedType, current selected type
        * @param{array} field, an array of field details
        * @param{object} referenceNode, parent node for dependent field
        * @param{int} index , field index
        * @memberOf widgets/geo-form/geo-form
        */
        _validateTypeFieldsValue: function (selectedType, field, referenceNode, index) {
            var switchDomainType, i;
            // check for domain values
            for (i in selectedType.domains) {
                if (selectedType.domains.hasOwnProperty(i)) {
                    //condition to find the domain properties for current field
                    if (i === field.name) {
                        switchDomainType = selectedType.domains[i].type || "codedValue";
                        switch (switchDomainType) {
                        case "inherited":
                            break;
                        case "codedValue":
                            if (!field.domain) {
                                field.domain = {};
                            }
                            field.domain.codedValues = selectedType.domains[i].codedValues;
                            break;
                        case "range":
                            // Condition to change the range domain values of field already having domain.
                            if (!field.domain) {
                                field.domain = {};
                            }
                            field.domain.minValue = selectedType.domains[i].minValue;
                            field.domain.maxValue = selectedType.domains[i].maxValue;
                            break;
                        }
                    }
                }
            }
            // executed when the input is already present
            if (dom.byId(field.name)) {
                this._resetSubTypeFields(field);
            }
            // create form elements for dependent values
            this._createFormElement(field, index, referenceNode);
            // assign reference node to the dependent values
            if (field.type === "esriFieldTypeDate" || ((field.type === "esriFieldTypeSingle" || field.type === "esriFieldTypeDouble" || field.type === "esriFieldTypeSmallInteger" || field.type === "esriFieldTypeInteger") && (field.domain && field.domain.type && field.domain.type === "range"))) {
                referenceNode = dom.byId(field.name).parentNode.parentNode;
            } else {
                referenceNode = dom.byId(field.name).parentNode;
            }
        },

        /**
        * Validate form field
        * @param{object} currentNode, apply validation on current node
        * @param{object} currentField, object of current field in the info pop
        * @param{Boolean} iskeyPress, set Boolean value true or false
        * @memberOf widgets/geo-form/geo-form
        */
        _validateField: function (currentNode, currentField, iskeyPress) {
            var inputType, inputValue, node, typeCastedInputValue, error, floatVal = /^[-+]?[0-9]+\.[0-9]+$/,
                targetNode = currentNode.currentTarget || currentNode.srcElement, decimal = /^[-+]?[0-9]+$/;
            // trim current value
            inputValue = lang.trim(targetNode.value);
            // get value of data-input-type
            inputType = domAttr.get(targetNode, "data-input-type");
            // check for the target node and assign the parent node value
            if ($(currentNode.target)) {
                node = $(currentNode.target.parentNode)[0];
            } else {
                node = $(targetNode.parentNode)[0];
            }
            // Set validation on the field by their types
            switch (inputType) {
            case "String":
                if (inputValue.length !== 0) {
                    this._validateUserInput(false, node, inputValue, iskeyPress);
                } else {
                    error = string.substitute(this.appConfig.i18n.geoform.invalidInputValue, {
                        openStrong: "<strong>",
                        closeStrong: "</strong>"
                    });
                    this._validateUserInput(error, node, inputValue, iskeyPress);
                }
                break;
            case "SmallInteger":
                typeCastedInputValue = parseInt(inputValue, 10);
                if ((inputValue.match(decimal) && typeCastedInputValue >= -32768 && typeCastedInputValue <= 32767) && inputValue.length !== 0) {
                    this._validateUserInput(false, node, inputValue);
                    this._setFormatToValue(currentField, typeCastedInputValue, currentNode.target);
                } else {
                    error = string.substitute(this.appConfig.i18n.geoform.invalidSmallNumber, {
                        openStrong: "<strong>",
                        closeStrong: "</strong>"
                    });
                    this._validateUserInput(error, node, inputValue, iskeyPress);
                }
                break;
            case "Integer":
                typeCastedInputValue = parseInt(inputValue, 10);
                if ((inputValue.match(decimal) && typeCastedInputValue >= -2147483648 && typeCastedInputValue <= 2147483647) && inputValue.length !== 0) {
                    this._validateUserInput(false, node, inputValue, iskeyPress);
                    this._setFormatToValue(currentField, typeCastedInputValue, currentNode.target);
                } else {
                    error = string.substitute(this.appConfig.i18n.geoform.invalidNumber, {
                        openStrong: "<strong>",
                        closeStrong: "</strong>"
                    });
                    this._validateUserInput(error, node, inputValue, iskeyPress);
                }
                break;
            case "Single":
                // zero or more occurrence of (+-) at the start of expression
                // at least one occurrence of digits between o-9
                // occurrence of .
                // at least one occurrence of digits between o-9 in the end
                typeCastedInputValue = parseFloat(inputValue);
                if (((inputValue.match(decimal) || inputValue.match(floatVal)) && typeCastedInputValue >= -3.4 * Math.pow(10, 38) && typeCastedInputValue <= 1.2 * Math.pow(10, 38)) && inputValue.length !== 0) {
                    this._validateUserInput(false, node, inputValue, iskeyPress);
                    this._setFormatToValue(currentField, typeCastedInputValue, currentNode.target);
                } else {
                    error = string.substitute(this.appConfig.i18n.geoform.invalidFloat, {
                        openStrong: "<strong>",
                        closeStrong: "</strong>"
                    });
                    this._validateUserInput(error, node, inputValue, iskeyPress);
                }
                break;
            case "Double":
                typeCastedInputValue = parseFloat(inputValue);
                if (((inputValue.match(decimal) || inputValue.match(floatVal)) && typeCastedInputValue >= -2.2 * Math.pow(10, 308) && typeCastedInputValue <= 1.8 * Math.pow(10, 38)) && inputValue.length !== 0) {
                    this._validateUserInput(false, node, inputValue, iskeyPress);
                    this._setFormatToValue(currentField, typeCastedInputValue, currentNode.target);
                } else {
                    error = string.substitute(this.appConfig.i18n.geoform.invalidDouble, {
                        openStrong: "<strong>",
                        closeStrong: "</strong>"
                    });
                    this._validateUserInput(error, node, inputValue, iskeyPress);
                }
                break;
            }
        },

        /**
        * Format input values
        * @param{object} currentField, current targeted field
        * @param{int} typeCastedInputValue , input integer value of field
        * @memberOf widgets/geo-form/geo-form
        */
        _setFormatToValue: function (currentField, typeCastedInputValue, node) {
            var toFixedValue;
            // check if field has format and digitSeparator is true
            if (currentField.format && currentField.format.digitSeparator) {
                // set format to the field and set toFixed value on focus out
                toFixedValue = typeCastedInputValue.toFixed(currentField.format.places);
                domAttr.set(node, "value", toFixedValue);
            }
        },

        /**
        * Clear form fields, reset the form and assign default value if it exists
        * @memberOf widgets/geo-form/geo-form
        */
        _clearFormFields: function () {
            var attachNode, node, index, currentFileInputID, fileChange, defaultDate;
            // remove error and success messages for each form field
            array.forEach(query(".form-control", this.domNode), lang.hitch(this, function (currentInput) {
                node = currentInput.parentElement;
                //Remove error message div
                //This logic is required for resetting geoform when user enters something wrong and clicks on Cancel button
                array.some(node.children, lang.hitch(this, function (currentNode) {
                    if (domClass.contains(currentNode, "errorMessage")) {
                        this._removeErrorNode(currentNode);
                        return true;
                    }
                }));
                if (domClass.contains(this.select_location.nextSibling, "errorMessage")) {
                    this._removeErrorNode(this.select_location.nextSibling);
                }
                this.locator._hideText();
                // Clear form fields
                if (!domClass.contains(currentInput, "selectDomain")) {
                    domAttr.set(currentInput, "value", "");
                    domClass.remove(node.lastChild, "is-invalid");
                    domClass.remove(node.lastChild, "is-valid");
                } else {
                    currentInput.options[0].selected = true;
                    domClass.remove(node.lastChild, "is-valid");
                    domClass.remove(node.lastChild, "is-invalid");
                }
            }));
            array.forEach(this.sortedFields, lang.hitch(this, function (currentInput) {
                if (!currentInput.isTypeDependent) {
                    return true;
                }
                // rest form field and show dependent field in the form
                this._resetSubTypeFields(currentInput);
            }));
            // clear error and success messages
            array.forEach(query(".geoFormQuestionare .input-group"), function (currentInput) {
                if (domClass.contains(currentInput, 'input-group date')) {
                    domClass.remove(currentInput.lastChild, "is-invalid");
                    domClass.remove(currentInput.lastChild, "is-valid");
                } else {
                    domClass.remove(currentInput.childNodes[2], "is-invalid");
                    domClass.remove(currentInput.childNodes[2], "is-valid");
                }
            }); 

            // clear attachments
            currentFileInputID = this._fileAttachmentCounter - 1;
            currentFileInputID = "geoFormAttachment" + currentFileInputID;
            attachNode = dom.byId(currentFileInputID);
            if (attachNode && attachNode.value) {
                // We are adding attachNode.value= "" again to clear the attachment text in Firefox
                domAttr.set(attachNode, "value", "");
                // Below line works in all the browsers except Firefox
                // Since attachNode.value= "" was not working in IE8, we added this code to clear the attachment text
                $(dom.byId(currentFileInputID)).replaceWith($(dom.byId(currentFileInputID)).clone(true));
                // again attach the change event as the node is cloned, so previous callback was not getting executed.
                fileChange = on(dom.byId(currentFileInputID), "change", lang.hitch(this, function (evt) {
                    fileChange.remove();
                    this._onFileSelected(evt);
                }));
            }
            // clear attachments
            this._clearAttachements();
            // Reset Form
            this.userForm.reset();
            // Set default Values and success to the controls
            array.forEach(query(".form-control", this.domNode), lang.hitch(this, function (currentInput) {
                for (index = 0; index < this.defaultValueArray.length; index++) {
                    if (this.defaultValueArray[index].id === currentInput.id) {
                        if (this.defaultValueArray[index].type === "esriFieldTypeDate" || this.defaultValueArray[index].type === "range") {
                            domClass.add(currentInput.parentElement.parentElement.childNodes[1].lastChild, "is-valid");
                        } else {
                            domClass.add(currentInput.parentElement.lastChild, "is-valid");
                        }
                        if (!domClass.contains(currentInput, "selectDomain")) {
                            domAttr.set(currentInput, "value", this.defaultValueArray[index].defaultValue);
                        }
                        if (this.defaultValueArray[index].type === "esriFieldTypeDate") {
                            var date = new Date(this.defaultValueArray[index].defaultValue);
                            // set current date to date field
                            $(currentInput.parentElement).data('DateTimePicker').date(date);
                        }
                    }
                }
            }));

            domConstruct.empty(query(".esriCTResultContainer")[0]);
            domClass.add(query(".esriCTResultContainer")[0], "esriCTHidden");
        },

        /**
        * Add calendar notation icon
        * @param{object} formContent, Parent Node to attached field
        * @param{string} imageIconClass,default class of image icon calendar
        * @memberOf widgets/geo-form/geo-form
        */
        _addNotationIcon: function (formContent, imageIconClass) {
            var inputIconGroupContainer, inputIconGroupAddOn;
            // create container for calendar for date time picker
            inputIconGroupContainer = domConstruct.create("div", {
                className: "input-group"
            }, formContent);
            inputIconGroupAddOn = domConstruct.create("span", {
                className: "input-group-addon"
            }, inputIconGroupContainer);
            domConstruct.create("span", {
                className: "glyphicon " + imageIconClass
            }, inputIconGroupAddOn);
            // return Value
            return inputIconGroupContainer;
        },

        /**
        * Reset subtype fields
        * @param{object} currentInput, parent node to destroy dependent field
        * @memberOf widgets/geo-form/geo-form
        */
        _resetSubTypeFields: function (currentInput) {
            if (currentInput.type === "esriFieldTypeDate" || ((currentInput.type === "esriFieldTypeSmallFloat" || currentInput.type === "esriFieldTypeSmallInteger" || currentInput.type === "esriFieldTypeDouble" || currentInput.type === "esriFieldTypeInteger") && (currentInput.domain && currentInput.domain.type && currentInput.domain.type === "range"))) {
                if (dom.byId(currentInput.name)) {
                    domConstruct.destroy(dom.byId(currentInput.name).parentNode.parentNode);
                }
            } else {
                if (dom.byId(currentInput.name)) {
                    domConstruct.destroy(dom.byId(currentInput.name).parentNode);
                }
            }
        },

        /**
        * Create datetime picker
        * @param{object} parentNode, parent node to attached date time picker
        * @param{Boolean} isRangeField, set flag true or false depends on range
        * @param{string} fieldname, name of the field
        * @param{object} currentField, object of Current Field Details
        * @memberOf widgets/geo-form/geo-form
        */
        _createDateField: function (parentNode, isRangeField, fieldname, currentField) {
            var dateInputField, picker, selectedDate, setDateFormat, minVlaue, maxValue, value;
            domClass.add(parentNode, "date");
            // create input container for DateTimePicker
            dateInputField = domConstruct.create("input", {
                type: "text",
                value: "",
                className: "form-control hasDatetimepicker",
                "data-input-type": "Date",
                "id": fieldname
            }, parentNode);
            // get date format
            if (currentField.format && currentField.format.dateFormat) {
                setDateFormat = this.appUtils.getDateFormat(currentField.format.dateFormat);
            }
            // on focus
            on(dateInputField, "focus", function () {
                if (!isRangeField) {
                    $(this.parentElement).data("DateTimePicker").show();
                }
            });
            // on blur
            on(dateInputField, "blur", function () {
                $(this.parentElement).data("DateTimePicker").hide();
            });
            // Attach datetime picker to the container
            $(parentNode).datetimepicker({
                useStrict: false,
                format: setDateFormat && setDateFormat.dateFormat ? setDateFormat.dateFormat : false,
                locale: kernel.locale
            }).on('dp.show', function (evt) {
                /*Update the date picker position and make sure it is always displayed outside its parent container*/
                var datePickerDialogBox, datePickerDialogBoxPosition;
                datePickerDialogBox = query(".bootstrap-datetimepicker-widget.dropdown-menu")[0];
                if (datePickerDialogBox) {
                    datePickerDialogBoxPosition = domGeom.position(datePickerDialogBox, true);
                    domConstruct.place(datePickerDialogBox, dojo.body(), "first");
                    domStyle.set(datePickerDialogBox, "position", "absolute");
                    domStyle.set(datePickerDialogBox, "top", (datePickerDialogBoxPosition.y + "px"));
                    domStyle.set(datePickerDialogBox, "left", (datePickerDialogBoxPosition.x + "px"));
                    domStyle.set(datePickerDialogBox, "height", (datePickerDialogBoxPosition.h + "px"));
                }
                if (isRangeField) {
                    value = new Date(query("input", this)[0].value);
                    minVlaue = new Date(currentField.domain.minValue);
                    maxValue = new Date(currentField.domain.maxValue);
                    if ((value > minVlaue && value > maxValue) || (value < minVlaue && value < maxValue)) {
                        query("input", this)[0].value = "";
                    }
                }
                // on Datetime picker show event
                picker = $(this).data('DateTimePicker');
                selectedDate = picker.date();
                if (selectedDate === null) {
                    query("input", this)[0].value = "";
                }
                if (query(".errorMessage", query(evt.target).parents(".geoFormQuestionare")[0])[0]) {
                    domConstruct.destroy(query(".errorMessage", query(evt.target).parents(".geoFormQuestionare")[0])[0]);
                }
                domClass.remove(evt.target.lastChild, "is-invalid");
                domClass.add(evt.target.lastChild, "is-valid");
                if (query("input", this)[0].value === "") {
                    domClass.remove(evt.target.lastChild, "is-valid");
                    domClass.remove(evt.target.lastChild, "is-invalid");
                }
            }).on('dp.error', function (evt) {
                // on error
                evt.target.value = '';
                domClass.remove(evt.target.lastChild, "is-valid");
                domClass.add(evt.target.lastChild, "is-invalid");
            }).on("dp.hide", function (evt) {
                // on Datetime picker hide event
                if (query("input", this)[0].value === "") {
                    domClass.remove(evt.target.lastChild, "is-valid");
                    domClass.remove(evt.target.lastChild, "is-invalid");
                }
            }).on('dp.change', function (evt) {
                // on change
                domClass.add(evt.target.lastChild, "is-valid");
                domClass.remove(evt.target.lastChild, "is-invalid");
            });
            // if isRangeField is set to true for range Domain value then assign maximum and minimum value to the date time picker
            if (isRangeField) {
                $(parentNode).data("DateTimePicker").maxDate(new Date(currentField.domain.maxValue));
                $(parentNode).data("DateTimePicker").minDate(new Date(currentField.domain.minValue));
            }
            // return Value
            return dateInputField;
        },

        /**
        * Validate form input
        * @param{string} error, error found in the node
        * @param{object} node, parent node to add and remove classes based on validation
        * @param{string} inputValue , input value
        * @param{string} iskeyPress, check for flag
        * @memberOf widgets/geo-form/geo-form
        */
        _validateUserInput: function (error, node, inputValue, iskeyPress) {
            if (query(".errorMessage", node)[0]) {
                domConstruct.destroy(query(".errorMessage", node)[0]);
            }
            if (!error || (inputValue.length === 0 && !domClass.contains(node, "mandatory"))) {
                domClass.add(node.lastChild, "is-valid");
                domClass.remove(node.lastChild, "is-invalid");
            } else {
                // On Error show error massage
                // Change the class of node
                this._showErrorMessageDiv(error, node.children[0]);
                domClass.add(node.lastChild, "is-invalid");
                domClass.remove(node.lastChild, "is-valid");
            }
            if (iskeyPress && inputValue.length === 0 && !domClass.contains(node, "mandatory")) {
                domClass.remove(node.lastChild, "is-invalid");
                domClass.remove(node.lastChild, "is-valid");
            }
        },

        /**
        * Resize map
        * @memberOf widgets/geo-form/geo-form
        */
        _resizeMap: function () {
            // If geoform is visible
            if (this.map && !domClass.contains(dom.byId('geoformContainer'), "esriCTHidden")) {
                // re position the map
                this.map.reposition();
                // re size the map
                this.map.resize();
            }
        },

        /**
        * Clear graphics
        * @memberOf widgets/geo-form/geo-form
        */
        _clearSubmissionGraphic: function () {
            // set geometry to the null
            this.addressGeometry = null;
            //clear graphics
            this._graphicsLayer.clear();
        },

        /**
        * Executed on submitting a form
        * @memberOf widgets/geo-form/geo-form
        */
        _submitForm: function () {
            var erroneousFields = [], errorMessage;
            // for all the fields in geo form
            array.forEach(query(".geoFormQuestionare", this.domNode), lang.hitch(this, function (currentField) {
                // to check for errors in form before submitting.
                if ((query(".form-control", currentField)[0])) {
                    // condition to check if the entered values are erroneous.
                    if (domClass.contains(currentField, "is-invalid") && query("select", currentField).length === 0) {
                        erroneousFields.push(currentField);
                    }
                    // condition to check if mandatory fields are kept empty.
                    if ((query(".form-control", currentField)[0].value === "" && domClass.contains(currentField, "mandatory"))) {
                        this._validateUserInput(this.appConfig.i18n.geoform.requiredFields, currentField, query(".form-control", currentField)[0].value, true);
                        erroneousFields.push(currentField);
                    } else if (domClass.contains(currentField, "mandatory")) {
                        this._validateUserInput(false, currentField, query(".form-control", currentField)[0].value, true);
                    }
                }
            }));
            // this statement will remove the error message div at first and then will be applied if a valid location is not selected
            this._removeErrorNode(this.select_location.nextSibling);
            // If any error found
            if (erroneousFields.length !== 0) {
                // Scroll to the erroneous field node
                this.currentGeoformNode.animate({
                    scrollTop: erroneousFields[0].offsetTop
                }, 1000, function () {
                    erroneousFields[0].childNodes[1].focus();
                });
                /*
                if (!this.addressGeometry && !this.isEdit) {
                    // error message
                    errorMessage = this.appConfig.i18n.geoform.selectLocation;
                    this._showErrorMessageDiv(errorMessage, this.select_location);
                }*/
            } else {
                // If geometry is selected on the map
                if (this.addressGeometry) {
                    // Add feature to the layer
                    this._addFeatureToLayer();
                } else {
                    if (this.isEdit) {
                        this.updateFeatureToLayer();
                    } else {
                        // error message
                        errorMessage = this.appConfig.i18n.geoform.selectLocation;
                        this._showErrorMessageDiv(errorMessage, this.select_location, "location");
                        // Scroll to the selected location
                        this.currentGeoformNode.animate({
                            scrollTop: this.select_location.offsetTop
                        }, 1000);
                        query(".errorMessage", this.locationSection)[0].focus();
                    }
                }
            }
        },

        /**
        * Add feature to layer
        * @memberOf widgets/geo-form/geo-form
        */
        _addFeatureToLayer: function () {
            var featureData, key, value, datePicker, picker, fileList, i, type, editedFields = [],
            attachmentsDef;
            attachmentsDef = new Deferred();
            // show loading indicator
            this.appUtils.showLoadingIndicator();
            // Create instance of graphic
            featureData = new Graphic();
            // create an empty array object
            featureData.attributes = {};
            // for all the fields
            //Limit scope the current domNode to avoid the conflicts
            array.forEach(query(".geoFormQuestionare .form-control", this.domNode),
                lang.hitch(this, function (currentField) {
                    if (currentField.value !== "") {
                        // get id of the field
                        key = domAttr.get(currentField, "id");
                        // check for datetime picker and assign value
                        if (domClass.contains(currentField, "hasDatetimepicker")) {
                            datePicker = $(currentField.parentNode).data('DateTimePicker').date();
                            if (datePicker) {
                                // need to get time of date in ms for service
                                value = datePicker.valueOf();
                            }
                        } else {
                            value = lang.trim(currentField.value);
                            //For the date domain, convert the epoc date in number if it is string
                            //This makes sure the correct data is being send for feature creation 
                            var fieldInfo = this.selectedLayer.getField(currentField.id);
                            if (fieldInfo && fieldInfo.type === "esriFieldTypeDate") {
                                if (currentField.value) {
                                    value = Number(currentField.value);
                                }
                            }
                        }
                        // Assign value to the attributes
                        featureData.attributes[key] = value;
                        editedFields.push(key);
                    }
                }));

            // If layer has ReportedBy Field then Add logged in username in it
            // Add ReportedBy field to editedFields array so that it will not get the default value from template
            if (this._layerHasReportedByField) {
                featureData.attributes[this.config.reportedByField] = this.config.logInDetails.processedUserName;
                editedFields.push(key);
            }
            //Check if layer has typeID field, then add the default values for the field from selected typeID field template
            if (this.layer.typeIdField && this.layer.typeIdField !== "") {
                for (type in this.layer.types) {
                    if (this.layer.types.hasOwnProperty(type)) {
                        if (this.layer.types[type].id === featureData.attributes[this.layer.typeIdField]) {
                            this._addValuesFromTemplate(this.layer.types[type].templates[0], editedFields, featureData);
                        }
                    }
                }
            }
            //Check if layer has template, then add the Default values from template of the layer, for the fields which are not editable(not available in geoform to edit)
            if (this.layer.templates && this.layer.templates.length > 0) {
                this._addValuesFromTemplate(this.layer.templates[0], editedFields, featureData);
            }

            featureData.geometry = {};
            // Assign feature geometry
            featureData.geometry = this.addressGeometry;
            //Check if location field is not visible in popup but still it is editable
            if (this.newLocationFieldValue) {
                featureData.attributes[this.config.locationField] = this.newLocationFieldValue;
            }
            // Add feature to the layer
            this.selectedLayer.applyEdits([featureData], null, null, lang.hitch(this, function (addResults) {
                // Add attachment on success
                if (addResults[0].success) {
                    //Filter file array
                    if (this._resizedFiles && this._resizedFiles.length > 0) {
                        this._resizedFiles = this._resizedFiles.filter(function (fileIndex) {
                            return fileIndex !== null;
                        });
                    }
                    //if layer has attachments then add those attachments
                    if (this.selectedLayer.hasAttachments &&
                        (query(".esriCTFileToSubmit", this.attachmentSection).length > 0 ||
                            this._resizedFiles.length > 0)) {
                        //get all the attachments and append it in form element
                        fileList = query(".esriCTFileToSubmit", this.attachmentSection);
                        //If new attachments which are reduced in the quality are added
                        //add them to the fileList array so that they are processed
                        if (this._resizedFiles.length > 0) {
                            array.forEach(this._resizedFiles, lang.hitch(this, function (formNode) {
                                if (formNode) {
                                    fileList.push(formNode);
                                }
                            }));
                        }
                        //reset fileAttached and failed counter
                        this._fileAttachedCounter = 0;
                        this._fileFailedCounter = 0;
                        //set total file attached counter
                        this._totalFileAttachedCounter = fileList.length;
                        for (i = 0; i < fileList.length; i++) {
                            //handle success and error callback for add attachments
                            this.selectedLayer.addAttachment(addResults[0].objectId, fileList[i],
                                lang.hitch(this, function () { this._onAttachmentUploadComplete(attachmentsDef); }),
                                lang.hitch(this, function () { this._onAttachmentUploadFailed(attachmentsDef); }));
                        }
                        //Call geoform submitted method to allow other widgets to update the
                        //feature instance and all necessary objects and components
                        attachmentsDef.then(lang.hitch(this, function () {
                            // Successfully feature is added on the layer
                            this.geoformSubmitted(addResults[0].objectId);
                        }));
                    } else {
                        // Successfully feature is added on the layer
                        this.geoformSubmitted(addResults[0].objectId);
                        //hide loading indicator started in _addFeatureToLayer method
                        this.appUtils.hideLoadingIndicator();
                        // reset form
                        this._clearFormFields();
                    }
                    // remove graphic
                    this._clearSubmissionGraphic();
                    if (this.changedExtent) {
                        this.map.setExtent(this.changedExtent);
                    } else {
                        //reset to default extent
                        if (this.defaultExtent) {
                            this.map.setExtent(this.defaultExtent);
                        }
                    }
                    // Show Thank you message on Success
                    this._showHeaderMessageDiv(this.config.submitMessage, "success");
                    if (this.appConfig.showNonEditableLayers) {
                        //Refresh label layers to fetch label of updated feature
                        this.appUtils.refreshLabelLayers(this._webmapResponse.itemInfo.itemData.operationalLayers);
                    }
                    //Refresh the layer on geoform map
                    this.updateLayerOnMap();
                } else {
                    domConstruct.destroy(query(".errorMessage")[0]);
                    // Show Error message on Failure
                    this._showHeaderMessageDiv(this.appConfig.i18n.geoform.errorsInApplyEdits, "error");
                    //hide loading indicator started in _addFeatureToLayer method
                    this.appUtils.hideLoadingIndicator();
                }
            }), lang.hitch(this, function () {
                // remove error
                domConstruct.destroy(query(".errorMessage")[0]);
                // Show error message on Failure
                this._showHeaderMessageDiv(this.appConfig.i18n.geoform.errorsInApplyEdits, "error");
                //hide loading indicator started in _addFeatureToLayer method
                this.appUtils.hideLoadingIndicator();
            }));
            return attachmentsDef.promise;
        },

        /**
        * Updated feature in layer
        * @memberOf widgets/geo-form/geo-form
        */
        updateFeatureToLayer: function () {
            var featureData, key, value, datePicker, picker, editedFields = [], i, fileList;
            // show loading indicator
            this.appUtils.showLoadingIndicator();
            // Create instance of graphic
            featureData = new Graphic();
            // create an empty array object
            featureData.attributes = {};
            // for all the fields
            //Limit scope the current domNode to avoid the conflicts
            array.forEach(query(".geoFormQuestionare .form-control", this.domNode), lang.hitch(this, function (currentField) {
                // get id of the field
                key = domAttr.get(currentField, "id");
                if (currentField.value !== "") {
                    // check for datetime picker and assign value
                    if (domClass.contains(currentField, "hasDatetimepicker")) {
                        picker = $(currentField.parentNode).data('DateTimePicker');
                        datePicker = picker.date();
                        if (datePicker) {
                            // need to get time of date in ms for service
                            value = datePicker.valueOf();
                        }
                    } else {
                        value = lang.trim(currentField.value);
                    }
                    // Assign value to the attributes
                    featureData.attributes[key] = value;
                    editedFields.push(key);
                } else if (this.isEdit) {
                    // Assign value to the attributes
                    featureData.attributes[key] = null;
                    editedFields.push(key);
                }
            }));

            // If layer has ReportedBy Field then Add logged in username in it
            // Add ReportedBy field to editedFields array so that it will not get the default value from template
            if (this._layerHasReportedByField) {
                featureData.attributes[this.config.reportedByField] = this.config.logInDetails.processedUserName;
                editedFields.push(key);
            }
            //add object id to featureData targetNode
            featureData.attributes[this.layer.objectIdField] = this.item.attributes[this.layer.objectIdField];
            // Add feature to the layer
            this.layer.applyEdits(null, [featureData], null, lang.hitch(this, function (addResults, updateResults, deleteResults) {
                //Filter file array
                if (this._resizedFiles && this._resizedFiles.length > 0) {
                    this._resizedFiles = this._resizedFiles.filter(function (fileIndex) {
                        return fileIndex !== null;
                    });
                }
                // Add attachment on success
                if (updateResults[0].success) {
                    //get all the attachments and append it in form element
                    fileList = query(".esriCTFileToSubmit", this.attachmentSection);
                    //check new images are being added and they are resized as per the
                    //configured dimensions
                    //Add them to the file list, so they will be considered while adding attachments
                    if (this._resizedFiles.length > 0) {
                        array.forEach(this._resizedFiles, lang.hitch(this, function (formNode) {
                            if (formNode) {
                                fileList.push(formNode);
                            }
                        }));
                    }
                    //if layer has attachments then add those attachments
                    if (this.layer.hasAttachments && (fileList.length > 0 || this._deletedAttachmentsPopupArr.length > 0)) {
                        if (fileList.length > 0) {
                            //reset fileAttached and failed counter
                            this._fileAttachedCounter = 0;
                            this._fileFailedCounter = 0;
                            //set total file attached counter
                            this._totalFileAttachedCounter = fileList.length;
                            for (i = 0; i < fileList.length; i++) {
                                //handle success and error callback for add attachments
                                this.layer.addAttachment(updateResults[0].objectId, fileList[i],
                                    lang.hitch(this, function () { this._onAttachmentUploadComplete(null); }),
                                    lang.hitch(this, function () { this._onAttachmentUploadFailed(null); }));
                            }
                        } else {
                            this._deleteAttachments();
                        }
                    } else {
                        //hide loading indicator started in _addFeatureToLayer method
                        this.appUtils.hideLoadingIndicator();
                        // reset form
                        this._clearFormFields();
                    }
                    //Change the selected item's attributes
                    lang.mixin(this.item.attributes, featureData.attributes);
                    //mixin makesure's all te other required atrributes are not overrriden
                    // Successfully feature is added on the layer
                    this.geoformFeatureUpdated(this.item);
                } else {
                    domConstruct.destroy(query(".errorMessage")[0]);
                    // Show Error message on Failure
                    this._showHeaderMessageDiv(this.appConfig.i18n.geoform.errorsInApplyEdits, "error");
                    //hide loading indicator
                    this.appUtils.hideLoadingIndicator();
                }
            }), lang.hitch(this, function () {
                // remove error
                domConstruct.destroy(query(".errorMessage")[0]);
                // Show error message on Failure
                this._showHeaderMessageDiv(this.appConfig.i18n.geoform.errorsInApplyEdits, "error");
                //hide loading indicator
                this.appUtils.hideLoadingIndicator();
            }));
        },

        /**
        * Add fields and values to feature data to be submitted, from template
        * @param{object} template object returned from layer info
        * @param{array} editedFields, fields which are edited and needs to be skipped for considering their default values
        * @param{object} featureData,Feature object to be submitted
        * @memberOf widgets/geo-form/geo-form
        */
        _addValuesFromTemplate: function (template, editedFields, featureData) {
            var fieldAttribute;
            //loop through all the fields in Templates and if the field has some value add that field to feature
            for (fieldAttribute in template.prototype.attributes) {
                if (template.prototype.attributes.hasOwnProperty(fieldAttribute)) {
                    //skip the fields which are edited using geoform
                    if ($.inArray(fieldAttribute, editedFields) === -1) {
                        //check if their is valid value for the field in template and add that value in feature-data to be submitted
                        //also add that field in edited array since same field can have default values in template for layer and template for typeIdFielf
                        if (template.prototype.attributes[fieldAttribute]) {
                            featureData.attributes[fieldAttribute] = template.prototype.attributes[fieldAttribute];
                            editedFields.push(fieldAttribute);
                        }
                    }
                }
            }
        },

        /**
        * Callback hander for attachment upload Complete event
        * @memberOf widgets/geo-form/geo-form
        */
        _onAttachmentUploadComplete: function (attachmentsDef) {
            this._fileAttachedCounter++;
            this._updateFileAttachedCounter(attachmentsDef);
        },

        /**
        * Callback hander for attachment upload failed event
        * @memberOf widgets/geo-form/geo-form
        */
        _onAttachmentUploadFailed: function (attachmentsDef) {
            this._fileFailedCounter++;
            this._updateFileAttachedCounter(attachmentsDef);
        },

        /**
        * On attachment upload
        * @memberOf widgets/geo-form/geo-form
        */
        _updateFileAttachedCounter: function (attachmentsDef) {
            if (this._totalFileAttachedCounter === (this._fileAttachedCounter + this._fileFailedCounter)) {
                if (this._deletedAttachmentsPopupArr.length > 0) {
                    this._deleteAttachments();
                } else {
                    this._onUpdateOperationComplete();
                }
                this._clearFormFields();
                if (attachmentsDef) {
                    attachmentsDef.resolve();
                }
            }
        },

        /**
        * This function is executed when all applicable comment operations like add, update or delete is completed
        * @memberOf widgets/geo-form/geo-form
        */
        _onUpdateOperationComplete: function () {
            var attachmentFailedMsg;
            this._clearAttachements();
            if (this._fileFailedCounter > 0) {
                attachmentFailedMsg = string.substitute(this.config.i18n.geoform.attachmentUploadStatus, {
                    "failed": this._fileFailedCounter,
                    "total": this._totalFileAttachedCounter
                });
                // Show Thank you message on Success
                this._showHeaderMessageDiv(attachmentFailedMsg);
                setTimeout(lang.hitch(this, function () {
                    // Successfully feature is added on the layer
                    this.geoformFeatureUpdated(this.item);
                }), 200);

            } else {
                setTimeout(lang.hitch(this, function () {
                    // Successfully feature is added on the layer
                    this.geoformFeatureUpdated(this.item);
                }), 200);
            }
            this.appUtils.hideLoadingIndicator();
            this._resizedFiles = [];
        },

        /**
        * Create error message container
        * @param{string} errorMessage, error massage which need to show on error
        * @param{object} errorMessageNode, node to bind error massage
        * @memberOf widgets/geo-form/geo-form
        */
        _showErrorMessageDiv: function (errorMessage, errorMessageNode, field) {
            var errorNodeContainer, fallBackTextNode, place = "after";
            if (errorMessageNode) {
                //this statement will remove the error message div at first and then will be applied if a valid location is not selected
                this._removeErrorNode(errorMessageNode.nextSibling);
            }
            //Create parent container for showing error message
            errorNodeContainer = domConstruct.create("div", {
                role: "alert",
                id: "errorMessage",
                "tabindex": "-1",
                className: "alert alert-danger errorMessage"
            }, null);
            domConstruct.create("span", {
                "aria-hidden": "true",
                innerHTML: errorMessage
            }, errorNodeContainer);
            fallBackTextNode = domConstruct.create("span", {
                "class": "esriCTFallBackText"
            }, errorNodeContainer);
            //Set the text based on validation field
            if (field && field === "location") {
                domAttr.set(fallBackTextNode, "innerHTML", errorMessage);
            } else {
                domAttr.set(fallBackTextNode, "innerHTML",
                    string.substitute(this.appConfig.i18n.geoform.errorMessageText, {
                        message: errorMessage,
                        fieldName: errorMessageNode.innerHTML
                    }));
            }
            domConstruct.place(errorNodeContainer, errorMessageNode, place);
            // if success message is visible hide it
            if (domClass.contains(this.headerMessageDiv, "esriCTVisible")) {
                domClass.replace(this.headerMessageDiv, "esriCTHidden", "esriCTVisible");
            }
            // resize map
            this._resizeMap();
            // hide Loading Indicator
            this.appUtils.hideLoadingIndicator();
        },

        /**
        * Remove the error message container.
        * @param{object} node, node to bind error message
        * @memberOf widgets/geo-form/geo-form
        */
        _removeErrorNode: function (node) {
            if (domClass.contains(node, "errorMessage")) {
                // destroy parent node
                domConstruct.destroy(node);
            }
        },

        /**
        * Display message on header of form
        * @param{string} innerText, massage which needs to show
        * @param{string} isError, set flag for error
        * @memberOf widgets/geo-form/geo-form
        */
        _showHeaderMessageDiv: function (innerText, isError) {
            // assign success or error handler class to the container
            if (isError === "success") {
                domClass.replace(this.headerMessageType, "alert-success", "alert-danger alert-warning");
            } else if (isError === "warning") {
                domClass.replace(this.headerMessageType, "alert-warning", "alert-danger alert-success");
            } else {
                domClass.replace(this.headerMessageType, "alert-danger", "alert-warning alert-success");
            }
            // replace node
            domClass.replace(this.headerMessageDiv, "esriCTVisible", "esriCTHidden");
            // set innerHTML
            domAttr.set(this.headerMessageContent, "innerHTML", innerText);
            // handle close button click of message
            on(this.headerMessageButton, "click", lang.hitch(this, function () {
                if (domClass.contains(this.headerMessageDiv, "esriCTVisible")) {
                    domClass.replace(this.headerMessageDiv, "esriCTHidden", "esriCTVisible");
                }
            }));
            // Scroll geoform to top
            this.currentGeoformNode.animate({
                scrollTop: 0
            }, 1000, lang.hitch(this, function () {
                this.headerMessageContent.focus();
            }));
            // resize map
            this._resizeMap();
        },

        /**
        * This function is used to close form
        * @memberOf widgets/geo-form/geo-form
        */
        closeForm: function (evt) {
            if (!this.appUtils.validateEvent(evt)) {
                return;
            }
            dom.byId("geoFormBody").scrollTop = 0;
            this.toolbar.deactivate();
            domClass.replace(dom.byId('geoformContainer'), "esriCTHidden", "esriCTVisible");
            //Hide header message div if it exist
            if (domClass.contains(this.headerMessageDiv, "esriCTVisible")) {
                domClass.replace(this.headerMessageDiv, "esriCTHidden", "esriCTVisible");
            }

            array.forEach(query('.errorMessage'), lang.hitch(this, function (node) {
                this._removeErrorNode(node);
            }));

            array.forEach(query('.geoFormQuestionare'), lang.hitch(this, function (currentNode) {
                if (domClass.contains(currentNode.lastChild, "is-invalid")) {
                    domClass.remove(currentNode.lastChild, "is-invalid");
                }
            }));
            this.onFormClose(evt);
        },

        /**
        * Active draw tool
        * @memberOf widgets/geo-form/geo-form
        */
        _activateDrawTool: function () {
            var tool, type;
            // Select layer type
            type = this._selectLayerType();
            tool = type.toUpperCase();
            // active draw tool
            this.toolbar.activate(Draw[tool]);
            // clear graphics on the map
            this._clearSubmissionGraphic();
        },

        /**
        * Get geometry type of the selected layer
        * @memberOf widgets/geo-form/geo-form
        */
        _selectLayerType: function () {
            var type;
            //set type for selected geometry type of the layer
            switch (this.layer.geometryType) {
            case "esriGeometryPoint":
                type = "point";
                break;
            case "esriGeometryPolyline":
                type = "polyline";
                break;
            case "esriGeometryPolygon":
                type = "polygon";
                break;
            }
            // return Value
            return type;
        },

        /**
        * Add graphic on the map
        * @param{object} evt, draw tool bar event
        * @memberOf widgets/geo-form/geo-form
        */
        _addToGraphicsLayer: function (evt, isReverseGeocodeRequired) {
            var symbol, graphic, graphicGeometry;
            // clear graphics on the map
            this._clearSubmissionGraphic();
            // get geometry
            if (evt.geometry) {
                graphicGeometry = evt.geometry.type === "extent" ? this._createPolygonFromExtent(evt.geometry) : evt.geometry;
            } else {
                graphicGeometry = evt;
            }
            // assign geometry to the variable
            this.addressGeometry = graphicGeometry;
            symbol = this._createFeatureSymbol(graphicGeometry.type);
            // create new graphic
            graphic = new Graphic(graphicGeometry, symbol);
            // add graphics
            this._graphicsLayer.add(graphic);
            if (isReverseGeocodeRequired) {
                if (evt.geometry.type === "point") {
                    this.appUtils.locatorInstance.locationToAddress(webMercatorUtils.webMercatorToGeographic(evt.geometry), 100);
                }
            }
        },

        /**
        * Zoom to the selected feature
        * @param{object} geometry, geometry of the graphics plotted on the map
        * @memberOf widgets/geo-form/geo-form
        */
        _zoomToSelectedFeature: function (geometry) {
            var centerPoint;
            // check for geometry type of different layer
            if (geometry.type === "point") {
                this.map.setLevel(this.appConfig.zoomLevel);
                this.map.centerAt(geometry);
            } else if (geometry.type === "polyline") {
                this.map.setLevel(this.appConfig.zoomLevel);
                centerPoint = geometry.getExtent();
                this.map.setExtent(centerPoint);
                // if geometry is of type polygon, add border to the polygon
            } else if (geometry.type === "polygon") {
                this.map.setLevel(this.appConfig.zoomLevel);
                centerPoint = geometry.getExtent();
                this.map.setExtent(centerPoint);
            }
        },
        /**
        * Convert extent type of geometry to polygon geometry
        * @param{object} geometry, geometry of the graphics plotted on the map
        * @memberOf widgets/geo-form/geo-form
        */
        _createPolygonFromExtent: function (geometry) {
            var polygon = new Polygon(geometry.spatialReference);
            // set geometry ring to the polygon layer
            polygon.addRing([
                [geometry.xmin, geometry.ymin],
                [geometry.xmin, geometry.ymax],
                [geometry.xmax, geometry.ymax],
                [geometry.xmax, geometry.ymin],
                [geometry.xmin, geometry.ymin]
            ]);
            // return polygon geometry
            return polygon;
        },

        /**
        * Create symbol for draw tool geometries in draw tab
        * @param{string} geometryType, type of geometry
        * @memberOf widgets/geo-form/geo-form
        */
        _createFeatureSymbol: function (geometryType) {
            var symbol;
            //set symbol for selected geometry type of the layer
            switch (geometryType) {
            case "point":
                symbol = new SimpleMarkerSymbol();
                break;
            case "polyline":
                symbol = new SimpleLineSymbol();
                break;
            case "polygon":
                symbol = new SimpleFillSymbol();
                break;
            }
            //return symbol
            return symbol;
        },

        /**
        * Executed when feature is added successfully on the layer
        * @param{string} isSubmitted, set flag for submitted form
        * @memberOf widgets/geo-form/geo-form
        */
        geoformSubmitted: function (isSubmitted) {
            // return value
            return isSubmitted;
        },

        /**
        * Executed when feature is updated successfully on the layer
        * @param{string} isEdited, set flag for edited form
        * @memberOf widgets/geo-form/geo-form
        */
        geoformFeatureUpdated: function (isEdited) {
            // return value
            return isEdited;
        },

        /**
        * Set extent of main map to geoform map
        * @memberOf widgets/geo-form/geo-form
        */
        setMapExtent: function (extent) {
            this.changedExtent = extent;
        },

        /**
        * Invoked when touch occurs on respective title
        * @memberOf geo-form/geo-form
        */
        _createTooltip: function (node, title) {
            domAttr.set(node, "data-original-title", title);
            //Remove previous handle
            if (this.tooltipHandler) {
                this.tooltipHandler.remove();
                if ($(node)) {
                    $(node).tooltip("hide");
                }
            }
            this.tooltipHandler = on(node, touch.press, lang.hitch(this, function (e) {
                $(node).tooltip("toggle");
                e.preventDefault();
            }));
            on(document, "click", lang.hitch(this, function () {
                $(node).tooltip("hide");
            }));

            on(window, "resize", lang.hitch(this, function () {
                $(node).tooltip("hide");
            }));
        },

        /**
        * This function is used to check if valid location field is set in configuration
        * @memberOf geo-form/geo-form
        */
        _findLocationField: function () {
            array.some(this.layer.fields, lang.hitch(this, function (currentField) {
                if (this.config.locationField === currentField.name && currentField.type === "esriFieldTypeString" && !currentField.domain && !currentField.typeField && currentField.editable) {
                    this.hasLocationField = true;
                    this.locationFieldLength = currentField.length;
                    return true;
                }
            }));
        },

        /**
        * This function is used to set selected address to location field
        * @memberOf widgets/geo-form/geo-form
        */
        _populateLocationField: function (selectedAddress) {
            var locationFieldTextBox = $("#geoformContainer").find("#" + this.config.locationField)[0];
            if (locationFieldTextBox && this.hasLocationField && this.config.locationField) {
                if (selectedAddress) {
                    locationFieldTextBox.value = selectedAddress;
                    domClass.add(locationFieldTextBox.parentElement.lastChild, "is-valid");
                }
                this.locator.txtSearch.value = selectedAddress;
            } else if (this.hasLocationField && this.config.locationField) {
                //Check if address fits in given field length
                if (selectedAddress.length <= this.locationFieldLength) {
                    this.newLocationFieldValue = selectedAddress;
                } else {
                    //If address is longer than the configured field length, trim the address till the field length
                    this.newLocationFieldValue = selectedAddress.substring(0, this.locationFieldLength);
                }
                this.locator.txtSearch.value = selectedAddress;
            }
        },

        /**
        * This function is used to reset location field
        * @memberOf widgets/geo-form/geo-form
        */
        _resetLocationField: function () {
            var locationFieldTextBox = $("#geoformContainer").find("#" + this.config.locationField)[0];
            if (locationFieldTextBox && this.hasLocationField && this.config.locationField) {
                locationFieldTextBox.value = "";
                domClass.remove(locationFieldTextBox.parentElement, "is-valid");
            }
            this.newLocationFieldValue = null;
            this.locator.txtSearch.value = "";
        },

        /* Section for fetching and showing existing attachments */

        /**
        * This function is used to fetch existing comment Attachments
        * @memberOf widgets/geo-form/geo-form
        */
        _fetchExistingAttachment: function () {
            var existingAttachmentObject;
            this._existingPopupAttachmentsArray = [];
            this.layer.queryAttachmentInfos(this.item.attributes[this.layer.objectIdField], lang.hitch(this, function (infos) {
                array.forEach(infos, lang.hitch(this, function (currentAttachment) {
                    existingAttachmentObject = {};
                    existingAttachmentObject.attachmentFileName = currentAttachment.name;
                    existingAttachmentObject.attachmentObjectID = currentAttachment.id;
                    this._existingPopupAttachmentsArray.push(existingAttachmentObject);
                    this._createExistingAttachment(existingAttachmentObject);
                    this._updateAttachmentCount();
                }));
            }));
        },

        /**
        * This function is used to create existing attachments
        * @param{object} existingAttachment - existing attachment whose UI needs to be created
        * @memberOf widgets/geo-form/geo-form
        */
        _createExistingAttachment: function (existingAttachment) {
            var alertHtml, existingAttachmentNode;
            alertHtml = "<div class=\"esriCTFileAlert alert alert-success fade in alert-dismissible show\">";
            alertHtml += "<button type=\"button\" class=\"close\" data-dismiss=\"alert\">" + "X" + "</button>";
            alertHtml += "<span>" + existingAttachment.attachmentFileName + "</span>";
            alertHtml += "</div>";
            existingAttachmentNode = domConstruct.toDom(alertHtml);
            domAttr.set(existingAttachmentNode.children[0], "attachmentObjectID", existingAttachment.attachmentObjectID);
            this._onExistingAttachmentCloseButtonClick(existingAttachmentNode.children[0]);
            domConstruct.place(existingAttachmentNode, this.fileAttachmentList, "last");
        },

        /**
        * This function is used to update the attachment count when clicked on existing attachment close button
        * @param{object} existingAttachmentCloseButton - close button to which click event needs to be attached
        * @memberOf widgets/geo-form/geo-form
        */
        _onExistingAttachmentCloseButtonClick: function (existingAttachmentCloseButton) {
            on(existingAttachmentCloseButton, "click", lang.hitch(this, function (evt) {
                setTimeout(lang.hitch(this, function () {
                    var attachmentObjectID;
                    attachmentObjectID = domAttr.get(evt.target, "attachmentObjectID");
                    attachmentObjectID = parseInt(attachmentObjectID, 10);
                    //Remove the deleted item from the DOM
                    if (evt.target.parentElement) {
                        domConstruct.destroy(evt.target.parentElement);
                    }
                    this._deletedAttachmentsPopupArr.push(attachmentObjectID);
                    this._updateAttachmentCount();
                }), 1);
            }));
        },

        /**
        * This function is used to delete the attachments
        * @memberOf widgets/geo-form/geo-form
        */
        _deleteAttachments: function () {
            this.layer.deleteAttachments(this.item.attributes[this.layer.objectIdField],
                this._deletedAttachmentsPopupArr,
                lang.hitch(this, this._onAttachmentDeleteComplete),
                lang.hitch(this, this._onAttachmentDeleteFailed));
        },

        /**
        * This function is called when attachments are deleted successfully
        * @memberOf widgets/geo-form/geo-form
        */
        _onAttachmentDeleteComplete: function (response) {
            this._onUpdateOperationComplete();
        },

        /**
        * This function is called when delete attachments operation is failed
        * @memberOf widgets/geo-form/geo-form
        */
        _onAttachmentDeleteFailed: function (err) {
            this._onUpdateOperationComplete();
        },

        /**
        * Update layer in the geoform map once a featue is deleted from main map
        * @memberOf widgets/geo-form/geo-form
        */
        updateLayerOnMap: function () {
            if (this.map && this.map._layers[this.layer.id]) {
                this.map._layers[this.layer.id].refresh();
            }
        },

        /**
        * Set location panel hint based on device dimensions
        * @memberOf widgets/geo-form/geo-form
        */
        setLocationPanelHint: function () {
            var pointLayerHintText, polygonLayerHintText;
            if (dojowindow.getBox().w < 768) {
                pointLayerHintText = this.appConfig.i18n.geoform.locationSelectionHintForPointLayer;
                polygonLayerHintText = this.appConfig.i18n.geoform.locationSelectionHintForPolygonLayer;
            } else {
                pointLayerHintText = this.appConfig.i18n.geoform.locationSelectionHintForPointLayerDesktop;
                polygonLayerHintText = this.appConfig.i18n.geoform.locationSelectionHintForPolygonLayerDesktop;
            }
            if (this.layer.geometryType === "esriGeometryPoint") {
                domAttr.set(this.locationHintTextNode, "innerHTML", pointLayerHintText);
            } else {
                domAttr.set(this.locationHintTextNode, "innerHTML", polygonLayerHintText);
            }
            domAttr.set(this.locationHintTextNode, "aria-label", pointLayerHintText);
        },

        onLocationSelected: function (geometry) {
            return geometry;
        }
    });
});