/// <summary>
 /// Конструктор.
 /// </summary>
 /// <param name="data">Данные.</param>
 /// <param name="engine">Движок.</param>
 public MakabaPageBannerViewModel(MakabaEntityTree data, string engine)
 {
     if (engine == null) throw new ArgumentNullException(nameof(engine));
     this.data = data;
     if (data?.BoardBannerImage != null)
     {
         BannerImageLink = new MediaLink()
         {
             Engine = engine,
             IsAbsolute = false,
             RelativeUri = data.BoardBannerImage
         };
     }
     if (data?.BoardBannerLink != null)
     {
         BannerLink = new BoardLink()
         {
             Engine = engine,
             Board = data.BoardBannerLink
         };
     }
     AppHelpers.DispatchAction(LoadBannerName);
 }
 /// <summary>
 /// Разобрать HTML в посте.
 /// </summary>
 /// <param name="data">Данные.</param>
 /// <param name="link">Ссылка.</param>
 /// <param name="isPreview">Предварительный просмотр треда.</param>
 /// <returns>Результат разбора.</returns>
 private PostTree Parse(BoardPost2 data, ThreadLink link, bool isPreview)
 {
     var ipIdRegex = Services.GetServiceOrThrow<IRegexCacheService>().CreateRegex(IpIdRegexText);
     var ipIdRegex2 = Services.GetServiceOrThrow<IRegexCacheService>().CreateRegex(IpIdRegexText2);
     var colorRegex = Services.GetServiceOrThrow<IRegexCacheService>().CreateRegex(ColorRegexText);
     PostFlags flags = 0;
     if (data.Banned != "0" && !string.IsNullOrWhiteSpace(data.Banned))
     {
         flags |= PostFlags.Banned;
     }
     if (data.Closed != "0" && !string.IsNullOrWhiteSpace(data.Closed))
     {
         flags |= PostFlags.Closed;
     }
     if (data.Sticky != "0" && !string.IsNullOrWhiteSpace(data.Sticky))
     {
         flags |= PostFlags.Sticky;
     }
     if (isPreview)
     {
         flags |= PostFlags.ThreadPreview;
     }
     if (data.Op != "0" && !string.IsNullOrWhiteSpace(data.Op))
     {
         flags |= PostFlags.Op;
     }
     if ("mailto:sage".Equals((data.Email ?? "").Trim(), StringComparison.OrdinalIgnoreCase))
     {
         flags |= PostFlags.Sage;
     }
     if (data.Edited != "0" && !string.IsNullOrWhiteSpace(data.Edited))
     {
         flags |= PostFlags.IsEdited;
     }
     string admName = null;
     if (data.Tripcode != null)
     {
         if (data.Tripcode.StartsWith("!!%") && data.Tripcode.EndsWith("%!!"))
         {
             if ("!!%mod%!!".Equals(data.Tripcode))
             {
                 admName = "## Mod ##";
             }
             else if ("!!%adm%!!".Equals(data.Tripcode))
             {
                 admName = "## Abu ##";
             }
             else if ("!!%Inquisitor%!!".Equals(data.Tripcode))
             {
                 admName = "## Applejack ##";
             }
             else if ("!!%coder%!!".Equals(data.Tripcode))
             {
                 admName = "## Кодер ##";
             }
             else admName = data.Tripcode.Replace("!!%", "## ").Replace("%!!", " ##");
             flags |= PostFlags.AdminTrip;
         }
     }
     var number = data.Number.TryParseWithDefault();
     var postNodes = Services.GetServiceOrThrow<IMakabaHtmlPostParseService>().GetPostNodes(data.Comment ?? "");
     var name = admName != null && string.IsNullOrWhiteSpace(data.Name) ? admName : WebUtility.HtmlDecode(data.Name ?? string.Empty).Replace("&nbsp;", " ");
     var result = new PostTree()
     {
         Link = new PostLink()
         {
             Engine = CoreConstants.Engine.Makaba,
             Board = link.Board,
             Thread = link.Thread,
             Post = number
         },
         ParentLink = new ThreadLink()
         {
             Engine = CoreConstants.Engine.Makaba,
             Board = link.Board,
             Thread = link.Thread
         },
         Subject = WebUtility.HtmlDecode(data.Subject ?? string.Empty),
         BoardSpecificDate = data.Date,
         Date = Services.GetServiceOrThrow<IDateService>().FromUnixTime(data.Timestamp.TryParseWithDefault()),
         Flags = flags,
         Quotes = new List<BoardLinkBase>(),
         Comment = new PostNodes()
         {
             Nodes = postNodes.ToList()
         },
         Hash = data.Md5,
         Extensions = new List<PostTreeExtension>(),
         Email = data.Email,
         Media = new List<PostMediaBase>(),
     };
     result.GeneratePostStamp();
     if (string.IsNullOrWhiteSpace(result.Subject) && number == link.Thread)
     {
         try
         {
             var lines = ToPlainText(result);
             if (lines.Count > 0)
             {
                 var s = lines.Where(l => !string.IsNullOrWhiteSpace(l)).FirstOrDefault();
                 if (s != null)
                 {
                     if (s.Length >= 50)
                     {
                         s = s.Substring(0, 50 - 3) + "...";
                     }
                     result.Subject = s;
                 }
             }
         }
         catch
         {
         }
     }
     string nameColor = null;
     PostColor color = null;
     var match = ipIdRegex.Match(name);
     var match2 = ipIdRegex2.Match(name);
     if (match.Success)
     {
         name = match.Groups["id"].Captures[0].Value;
     }
     else if (match2.Success)
     {
         name = match2.Groups["id"].Captures[0].Value;
         nameColor = match2.Groups["style"].Captures[0].Value;
         var cmatch = colorRegex.Match(nameColor);
         if (cmatch.Success)
         {
             try
             {
                 var r = byte.Parse(cmatch.Groups["r"].Captures[0].Value, CultureInfo.InvariantCulture.NumberFormat);
                 var g = byte.Parse(cmatch.Groups["g"].Captures[0].Value, CultureInfo.InvariantCulture.NumberFormat);
                 var b = byte.Parse(cmatch.Groups["b"].Captures[0].Value, CultureInfo.InvariantCulture.NumberFormat);
                 color = new PostColor() { R = r, G = g, B = b };
             }
             catch (Exception)
             {
                 color = null;
             }
         }
     }
     else if (name.StartsWith("Аноним ID:", StringComparison.OrdinalIgnoreCase))
     {
         name = name.Remove(0, "Аноним ID:".Length).Trim();
     }
     if (!string.IsNullOrEmpty(name) || !string.IsNullOrWhiteSpace(data.Tripcode))
     {
         result.Extensions.Add(new PostTreePosterExtension()
         {
             Name = HtmlToText(name ?? ""),
             /* Windows.Data.Html.HtmlUtilities.ConvertToText работает очень медленно, потому что вызывает EDGE через COM, не годится для массовых обработок*/
             //Name = Windows.Data.Html.HtmlUtilities.ConvertToText(name ?? ""),
             Tripcode = data.Tripcode,
             NameColorStr = nameColor,
             NameColor = color
         });
     }
     var flagAndIcon = ParseFlagAndIcon(data.Icon);
     if (flagAndIcon.Icon != null)
     {
         result.Extensions.Add(flagAndIcon.Icon);
     }
     if (flagAndIcon.Country != null)
     {
         result.Extensions.Add(flagAndIcon.Country);
     }
     if (!string.IsNullOrWhiteSpace(data.Tags))
     {
         result.Extensions.Add(new PostTreeTagsExtension()
         {
             TagString = data.Tags,
             Tags = new List<string>() { data.Tags }
         });
     }
     if (data.Files != null)
     {
         foreach (var f in data.Files)
         {
             BoardLinkBase mediaLink, tnLink;
             if (IsBoardLink(f.Path, link.Board))
             {
                 mediaLink = new BoardMediaLink()
                 {
                     Engine = CoreConstants.Engine.Makaba,
                     Board = link.Board,
                     RelativeUri = RemoveBoardFromLink(f.Path),
                     KnownMediaType = f.Type == MakabaMediaTypes.Webm ? KnownMediaType.Webm : KnownMediaType.Default
                 };
                 tnLink = new BoardMediaLink()
                 {
                     Engine = CoreConstants.Engine.Makaba,
                     Board = link.Board,
                     RelativeUri = RemoveBoardFromLink(f.Thumbnail),
                     KnownMediaType = KnownMediaType.Default
                 };
             }
             else
             {
                 mediaLink = new MediaLink()
                 {
                     Engine = CoreConstants.Engine.Makaba,
                     RelativeUri = f.Path,
                     IsAbsolute = false,
                 };
                 tnLink = new MediaLink()
                 {
                     Engine = CoreConstants.Engine.Makaba,
                     RelativeUri = f.Thumbnail,
                     IsAbsolute = false
                 };
             }
             var media = new PostImageWithThumbnail()
             {
                 Link = mediaLink,
                 ParentLink = result.Link,
                 Size = f.Size * 1024,
                 Height = f.Heigth,
                 Width = f.Width,
                 Name = f.Name,
                 Thumbnail = new PostImage()
                 {
                     Link = tnLink,
                     ParentLink = mediaLink,
                     Height = f.TnHeight,
                     Width = f.TnWidth,
                     Size = null,
                     Name = f.Name
                 }
             };
             result.Media.Add(media);
         }
     }
     return result;
 }
 private async Task DoLoad(BoardLinkBase link)
 {
     if (link == null) throw new ArgumentNullException(nameof(link));
     lastLink = link;
     if (!CanLoad)
     {
         return;
     }
     IsLoading = true;
     HasImage = false;
     NotNeeded = false;
     CanLoad = false;
     EntryBox.Text = "";
     try
     {
         var engines = ServiceLocator.Current.GetServiceOrThrow<INetworkEngines>();
         var engine = engines.GetEngineById(CoreConstants.Engine.Makaba);
         var captchaKeyOperation = engine.GetCaptchaKeys(link, CaptchaType.DvachCaptcha);
         var keysData = await captchaKeyOperation.Complete();
         if (!keysData.NeedCaptcha)
         {
             NotNeeded = true;
             return;
         }
         var keys = keysData.Keys as DvachCaptchaKeys;
         if (keys == null)
         {
             throw new InvalidOperationException("Неправильный тип ключей капчи");
         }
         var imgLink = new MediaLink() { Engine = CoreConstants.Engine.Makaba, IsAbsolute = false, RelativeUri = $"api/captcha/2chaptcha/image/{WebUtility.HtmlDecode(keys.Key)}" };
         this.key = keys.Key;
         var imgOperation = engine.GetMediaFile(imgLink);
         var img = await imgOperation.Complete();
         using (var str = await img.TempFile.OpenReadAsync())
         {
             var bitmap = new BitmapImage();
             await bitmap.SetSourceAsync(str);
             Image = bitmap;
             HasImage = true;
         }
         try
         {
             await img.TempFile.DeleteAsync();
         }
         catch (Exception ex)
         {
             DebugHelper.BreakOnError(ex);
         }
     }
     finally
     {
         IsLoading = false;
         CanLoad = true;
     }
 }
 /// <summary>
 /// Ссылка на медиа.
 /// </summary>
 /// <param name="link">Ссылка.</param>
 /// <returns>Ссылка.</returns>
 public Uri GetMediaLink(MediaLink link)
 {
     if (link.IsAbsolute)
     {
         return new Uri(link.RelativeUri, UriKind.Absolute);
     }
     else
     {
         return new Uri(BaseUri, link.RelativeUri);
     }
 }