internal override void FromHtml(IElement element)
        {
            base.FromHtml(element);

            // Set/update dataVersion if it was provided as html attribute
            var webPartDataVersion = element.GetAttribute(WebPartDataVersionAttribute);

            if (!string.IsNullOrEmpty(webPartDataVersion))
            {
                this.dataVersion = element.GetAttribute(WebPartDataVersionAttribute);
            }

            // load data from the data-sp-controldata attribute
            var jsonSerializerSettings = new JsonSerializerSettings()
            {
                MissingMemberHandling = MissingMemberHandling.Ignore
            };

            this.spControlData = JsonConvert.DeserializeObject <ClientSideWebPartControlData>(element.GetAttribute(CanvasControl.ControlDataAttribute), jsonSerializerSettings);
            this.controlType   = this.spControlData.ControlType;

            var wpDiv = element.GetElementsByTagName("div").Where(a => a.HasAttribute(ClientSideWebPart.WebPartDataAttribute)).FirstOrDefault();

            // Some components on the page (e.g. web parts configured with isDomainIsolated = true) are rendered differently in the HTML
            if (wpDiv == null)
            {
                JObject wpJObject = JObject.Parse(element.GetAttribute(CanvasControl.ControlDataAttribute));
                if (wpJObject["webPartData"] != null)
                {
                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["title"] != null)
                    {
                        this.title = wpJObject["webPartData"]["title"].Value <string>();
                    }
                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["description"] != null)
                    {
                        this.description = wpJObject["webPartData"]["description"].Value <string>();
                    }
                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["properties"] != null)
                    {
                        this.PropertiesJson = wpJObject["webPartData"]["properties"].ToString(Formatting.None);
                    }
                    // Check for fullbleed supporting web parts
                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["properties"] != null && wpJObject["webPartData"]["properties"]["isFullWidth"] != null)
                    {
                        this.supportsFullBleed = wpJObject["webPartData"]["properties"]["isFullWidth"].Value <Boolean>();
                    }

                    // Store the server processed content as that's needed for full fidelity
                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["serverProcessedContent"] != null && wpJObject["webPartData"]["serverProcessedContent"].ToString() != "")
                    {
                        this.serverProcessedContent = (JObject)wpJObject["webPartData"]["serverProcessedContent"];
                    }

                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["dynamicDataPaths"] != null)
                    {
                        this.dynamicDataPaths = (JObject)wpJObject["webPartData"]["dynamicDataPaths"];
                    }

                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["dynamicDataValues"] != null)
                    {
                        this.dynamicDataValues = (JObject)wpJObject["webPartData"]["dynamicDataValues"];
                    }

                    if (wpJObject["webPartData"] != null && wpJObject["webPartData"]["id"] != null)
                    {
                        this.webPartId = wpJObject["webPartData"]["id"].Value <string>();
                    }
                }
                else
                {
                    // Header controls (like in topic pages)
                    if (wpJObject["title"] != null)
                    {
                        this.title = wpJObject["title"].Value <string>();
                    }
                    if (wpJObject["description"] != null)
                    {
                        this.description = wpJObject["description"].Value <string>();
                    }
                    if (wpJObject["properties"] != null)
                    {
                        this.PropertiesJson = wpJObject["properties"].ToString(Formatting.None);
                    }
                    // Check for fullbleed supporting web parts
                    if (wpJObject["properties"] != null && wpJObject["properties"]["isFullWidth"] != null)
                    {
                        this.supportsFullBleed = wpJObject["properties"]["isFullWidth"].Value <Boolean>();
                    }

                    // Store the server processed content as that's needed for full fidelity
                    if (wpJObject["serverProcessedContent"] != null && wpJObject["serverProcessedContent"].ToString() != "")
                    {
                        this.serverProcessedContent = (JObject)wpJObject["serverProcessedContent"];
                    }

                    // Set/update dataVersion if it was set in the json data
                    if (wpJObject["dataVersion"] != null && !string.IsNullOrEmpty(wpJObject["dataVersion"].ToString(Formatting.None)))
                    {
                        this.dataVersion = wpJObject["dataVersion"].ToString(Formatting.None).Trim('"');
                    }

                    if (wpJObject["id"] != null)
                    {
                        this.webPartId = wpJObject["id"].Value <string>();
                    }

                    if (wpJObject["instanceId"] != null)
                    {
                        if (Guid.TryParse(wpJObject["instanceId"].Value <string>(), out Guid instanceGuid))
                        {
                            this.instanceId = instanceGuid;
                        }
                    }
                }
                this.usingSpControlDataOnly = true;
            }
            else
            {
                this.webPartData = wpDiv.GetAttribute(ClientSideWebPart.WebPartAttribute);

                // Decode the html encoded string
                var     decoded   = WebUtility.HtmlDecode(wpDiv.GetAttribute(ClientSideWebPart.WebPartDataAttribute));
                JObject wpJObject = JObject.Parse(decoded);
                this.title       = wpJObject["title"] != null ? wpJObject["title"].Value <string>() : "";
                this.description = wpJObject["description"] != null ? wpJObject["description"].Value <string>() : "";
                // Set property to trigger correct loading of properties
                this.PropertiesJson = wpJObject["properties"].ToString(Formatting.None);

                // Set/update dataVersion if it was set in the json data
                if (!string.IsNullOrEmpty(wpJObject["dataVersion"].ToString(Formatting.None)))
                {
                    this.dataVersion = wpJObject["dataVersion"].ToString(Formatting.None).Trim('"');
                }

                // Check for fullbleed supporting web parts
                if (wpJObject["properties"] != null && wpJObject["properties"]["isFullWidth"] != null)
                {
                    this.supportsFullBleed = wpJObject["properties"]["isFullWidth"].Value <Boolean>();
                }

                // Store the server processed content as that's needed for full fidelity
                if (wpJObject["serverProcessedContent"] != null && wpJObject["serverProcessedContent"].ToString() != "")
                {
                    this.serverProcessedContent = (JObject)wpJObject["serverProcessedContent"];
                }

                if (wpJObject["dynamicDataPaths"] != null)
                {
                    this.dynamicDataPaths = (JObject)wpJObject["dynamicDataPaths"];
                }

                if (wpJObject["dynamicDataValues"] != null)
                {
                    this.dynamicDataValues = (JObject)wpJObject["dynamicDataValues"];
                }

                this.webPartId = wpJObject["id"].Value <string>();

                var wpHtmlProperties = wpDiv.GetElementsByTagName("div").Where(a => a.HasAttribute(ClientSideWebPart.WebPartHtmlPropertiesAttribute)).FirstOrDefault();
                this.htmlPropertiesData = wpHtmlProperties.InnerHtml;
                this.htmlProperties     = wpHtmlProperties.GetAttribute(ClientSideWebPart.WebPartHtmlPropertiesAttribute);
            }
        }
        /// <summary>
        /// Returns a HTML representation of the client side web part
        /// </summary>
        /// <param name="controlIndex">The sequence of the control inside the section</param>
        /// <returns>HTML representation of the client side web part</returns>
        public override string ToHtml(float controlIndex)
        {
            if (!IsHeaderControl)
            {
                // Can this control be hosted in this section type?
                if (this.Section.Type == CanvasSectionTemplate.OneColumnFullWidth)
                {
                    if (!this.SupportsFullBleed)
                    {
                        throw new Exception("You cannot host this web part inside a one column full width section, only webparts that support full bleed are allowed");
                    }
                }

                ClientSideWebPartControlData controlData = null;
                if (this.usingSpControlDataOnly)
                {
                    controlData = new ClientSideWebPartControlDataOnly();
                }
                else
                {
                    controlData = new ClientSideWebPartControlData();
                }

                // Obtain the json data
                controlData.ControlType = this.ControlType;
                controlData.Id          = this.InstanceId.ToString("D");
                controlData.WebPartId   = this.WebPartId;
                controlData.Position    = new ClientSideCanvasControlPosition()
                {
                    ZoneIndex     = this.Section.Order,
                    SectionIndex  = this.Column.Order,
                    SectionFactor = this.Column.ColumnFactor,
                    LayoutIndex   = this.Column.LayoutIndex,
                    ControlIndex  = controlIndex,
                };

                if (this.section.Type == CanvasSectionTemplate.OneColumnVerticalSection)
                {
                    if (this.section.Columns.First().Equals(this.Column))
                    {
                        controlData.Position.SectionFactor = 12;
                    }
                }

                controlData.Emphasis = new ClientSideSectionEmphasis()
                {
                    ZoneEmphasis = this.Column.VerticalSectionEmphasis.HasValue ? this.Column.VerticalSectionEmphasis.Value : this.Section.ZoneEmphasis,
                };

                // Set the control's data version to the latest version...default was 1.0, but some controls use a higher version
                var webPartType = ClientSidePage.NameToClientSideWebPartEnum(controlData.WebPartId);

                // if we read the control from the page then the value might already be set to something different than 1.0...if so, leave as is
                if (this.DataVersion == "1.0")
                {
                    if (webPartType == DefaultClientSideWebParts.Image)
                    {
                        this.dataVersion = "1.8";
                    }
                    else if (webPartType == DefaultClientSideWebParts.ImageGallery)
                    {
                        this.dataVersion = "1.6";
                    }
                    else if (webPartType == DefaultClientSideWebParts.People)
                    {
                        this.dataVersion = "1.2";
                    }
                    else if (webPartType == DefaultClientSideWebParts.DocumentEmbed)
                    {
                        this.dataVersion = "1.1";
                    }
                    else if (webPartType == DefaultClientSideWebParts.ContentRollup)
                    {
                        this.dataVersion = "2.1";
                    }
                    else if (webPartType == DefaultClientSideWebParts.QuickLinks)
                    {
                        this.dataVersion = "2.0";
                    }
                }

                // Set the web part preview image url
                if (this.ServerProcessedContent != null && this.ServerProcessedContent["imageSources"] != null)
                {
                    foreach (JProperty property in this.ServerProcessedContent["imageSources"])
                    {
                        if (!string.IsNullOrEmpty(property.Value.ToString()))
                        {
                            this.webPartPreviewImage = property.Value.ToString().ToLower();
                            break;
                        }
                    }
                }

                ClientSideWebPartData webpartData = new ClientSideWebPartData()
                {
                    Id = controlData.WebPartId, InstanceId = controlData.Id, Title = this.Title, Description = this.Description, DataVersion = this.DataVersion, Properties = "jsonPropsToReplacePnPRules", DynamicDataPaths = "jsonDynamicDataPathsToReplacePnPRules", DynamicDataValues = "jsonDynamicDataValuesToReplacePnPRules", ServerProcessedContent = "jsonServerProcessedContentToReplacePnPRules"
                };

                if (this.usingSpControlDataOnly)
                {
                    (controlData as ClientSideWebPartControlDataOnly).WebPartData = "jsonWebPartDataToReplacePnPRules";
                    this.jsonControlData = JsonConvert.SerializeObject(controlData);
                    this.jsonWebPartData = JsonConvert.SerializeObject(webpartData);
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonPropsToReplacePnPRules\"", this.Properties.ToString(Formatting.None));
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonServerProcessedContentToReplacePnPRules\"", this.ServerProcessedContent.ToString(Formatting.None));
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonDynamicDataPathsToReplacePnPRules\"", this.DynamicDataPaths.ToString(Formatting.None));
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonDynamicDataValuesToReplacePnPRules\"", this.DynamicDataValues.ToString(Formatting.None));
                    this.jsonControlData = this.jsonControlData.Replace("\"jsonWebPartDataToReplacePnPRules\"", this.jsonWebPartData);
                }
                else
                {
                    this.jsonControlData = JsonConvert.SerializeObject(controlData);
                    this.jsonWebPartData = JsonConvert.SerializeObject(webpartData);
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonPropsToReplacePnPRules\"", this.Properties.ToString(Formatting.None));
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonServerProcessedContentToReplacePnPRules\"", this.ServerProcessedContent.ToString(Formatting.None));
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonDynamicDataPathsToReplacePnPRules\"", this.DynamicDataPaths.ToString(Formatting.None));
                    this.jsonWebPartData = this.jsonWebPartData.Replace("\"jsonDynamicDataValuesToReplacePnPRules\"", this.DynamicDataValues.ToString(Formatting.None));
                }
            }
            else
            {
                HeaderControlData webpartData = new HeaderControlData()
                {
                    Id = this.WebPartId, InstanceId = this.InstanceId.ToString("D"), Title = this.Title, Description = this.Description, DataVersion = this.DataVersion, Properties = "jsonPropsToReplacePnPRules", ServerProcessedContent = "jsonServerProcessedContentToReplacePnPRules"
                };
                this.canvasDataVersion = this.DataVersion;
                this.jsonWebPartData   = JsonConvert.SerializeObject(webpartData);
                this.jsonWebPartData   = this.jsonWebPartData.Replace("\"jsonPropsToReplacePnPRules\"", this.Properties.ToString(Formatting.None));
                this.jsonWebPartData   = this.jsonWebPartData.Replace("\"jsonServerProcessedContentToReplacePnPRules\"", this.ServerProcessedContent.ToString(Formatting.None));
                this.jsonControlData   = this.jsonWebPartData;
            }

            StringBuilder html = new StringBuilder(100);

            if (this.usingSpControlDataOnly || IsHeaderControl)
            {
                html.Append($@"<div {CanvasControlAttribute}=""{this.CanvasControlData}"" {CanvasDataVersionAttribute}=""{this.DataVersion}"" {ControlDataAttribute}=""{this.JsonControlData.Replace("\"", "&quot;")}""></div>");
            }
            else
            {
                html.Append($@"<div {CanvasControlAttribute}=""{this.CanvasControlData}"" {CanvasDataVersionAttribute}=""{this.DataVersion}"" {ControlDataAttribute}=""{this.JsonControlData.Replace("\"", "&quot;")}"">");
                html.Append($@"<div {WebPartAttribute}=""{this.WebPartData}"" {WebPartDataVersionAttribute}=""{this.DataVersion}"" {WebPartDataAttribute}=""{this.JsonWebPartData.Replace("\"", "&quot;").Replace("<", "&lt;").Replace(">", "&gt;")}"">");
                html.Append($@"<div {WebPartComponentIdAttribute}="""">");
                html.Append(this.WebPartId);
                html.Append("</div>");
                html.Append($@"<div {WebPartHtmlPropertiesAttribute}=""{this.HtmlProperties}"">");
                RenderHtmlProperties(ref html);
                html.Append("</div>");
                html.Append("</div>");
                html.Append("</div>");
            }
            return(html.ToString());
        }