/// <summary> /// /// </summary> /// <param name="obj"></param> /// <returns></returns> public override bool Equals(Object obj) { if (this == obj) { return(true); } if (!(obj is TweetEntity)) { return(false); } TweetEntity other = (TweetEntity)obj; if (Type.Equals(other.Type) && Start == other.Start && End == other.End && Value.Equals(other.Value)) { return(true); } else { return(false); } }
/// <summary> /// /// </summary> /// <param name="entities"></param> private void RemoveOverlappingEntities(List <TweetEntity> entities) { // Sort by index entities.Sort(new StartIndexComparer()); // Remove overlapping entities. // Two entities overlap only when one is URL and the other is hashtag/mention // which is a part of the URL. When it happens, we choose URL over hashtag/mention // by selecting the one with smaller start index. List <TweetEntity> toRemove = new List <TweetEntity>(); if (entities.Count > 0) { IEnumerator <TweetEntity> it = entities.GetEnumerator(); it.MoveNext(); TweetEntity prev = it.Current; while (it.MoveNext()) { TweetEntity cur = it.Current; if (prev.End > cur.Start) { toRemove.Add(cur); } else { prev = cur; } } foreach (TweetEntity remove in toRemove) { entities.Remove(remove); } } }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="text"></param> /// <param name="builder"></param> public void LinkToCashtag(TweetEntity entity, string text, StringBuilder builder) { string cashtag = entity.Value; IDictionary <string, string> attrs = new Dictionary <string, string>(); attrs["href"] = CashtagUrlBase + cashtag; attrs["title"] = "$" + cashtag; attrs["class"] = CashtagClass; LinkToTextWithSymbol(entity, "$", cashtag, attrs, builder); }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="symbol"></param> /// <param name="text"></param> /// <param name="attributes"></param> /// <param name="builder"></param> public void LinkToTextWithSymbol(TweetEntity entity, string symbol, string text, IDictionary <string, string> attributes, StringBuilder builder) { string taggedSymbol = string.IsNullOrWhiteSpace(SymbolTag) ? symbol : string.Format("<{0}>{1}</{0}>", SymbolTag, symbol); text = EscapeHTML(text); string taggedText = string.IsNullOrWhiteSpace(TextWithSymbolTag) ? text : string.Format("<{0}>{1}</{0}>", TextWithSymbolTag, text); bool includeSymbol = UsernameIncludeSymbol || !Regex.AT_SIGNS.IsMatch(symbol); if (includeSymbol) { LinkToText(entity, taggedSymbol.ToString() + taggedText, attributes, builder); } else { builder.Append(taggedSymbol); LinkToText(entity, taggedText, attributes, builder); } }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="text"></param> /// <param name="builder"></param> public void LinkToMentionAndList(TweetEntity entity, string text, StringBuilder builder) { string mention = entity.Value; // Get the original at char from text as it could be a full-width char. string atChar = text.Substring(entity.Start, 1); IDictionary <string, string> attrs = new Dictionary <string, string>(); if (entity.ListSlug != null) { mention += entity.ListSlug; attrs["class"] = ListClass; attrs["href"] = ListUrlBase + mention; } else { attrs["class"] = UsernameClass; attrs["href"] = UsernameUrlBase + mention; } LinkToTextWithSymbol(entity, atChar, mention, attrs, builder); }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="text"></param> /// <param name="builder"></param> public void LinkToHashtag(TweetEntity entity, string text, StringBuilder builder) { // Get the original hash char from text as it could be a full-width char. string hashChar = text.Substring(entity.Start, 1); string hashtag = entity.Value; IDictionary <string, string> attrs = new Dictionary <string, string>(); attrs["href"] = HashtagUrlBase + hashtag; attrs["title"] = "#" + hashtag; if (Regex.RTL_CHARACTERS.IsMatch(text)) { attrs["class"] = HashtagClass + " rtl"; } else { attrs["class"] = HashtagClass; } LinkToTextWithSymbol(entity, hashChar, hashtag, attrs, builder); }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="text"></param> /// <param name="attributes"></param> /// <param name="builder"></param> public void LinkToText(TweetEntity entity, string text, IDictionary <string, string> attributes, StringBuilder builder) { if (NoFollow) { attributes["rel"] = "nofollow"; } if (LinkAttributeModifier != null) { LinkAttributeModifier.Modify(entity, attributes); } if (LinkTextModifier != null) { text = LinkTextModifier.Modify(entity, text); } // append <a> tag builder.Append("<a"); foreach (var entry in attributes) { builder.Append(" ").Append(EscapeHTML(entry.Key)).Append("=\"").Append(EscapeHTML(entry.Value)).Append("\""); } builder.Append(">").Append(text).Append("</a>"); }
/// <summary> /// /// </summary> /// <param name="entity"></param> /// <param name="text"></param> /// <param name="builder"></param> public void LinkTorUrl(TweetEntity entity, string text, StringBuilder builder) { string url = entity.Value; string linkText = EscapeHTML(url); if (entity.DisplayUrl != null && entity.ExpandedUrl != null) { // Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste // should contain the full original URL (expanded_url), not the display URL. // // Method: Whenever possible, we actually emit HTML that contains expanded_url, and use // font-size:0 to hide those parts that should not be displayed (because they are not part of display_url). // Elements with font-size:0 get copied even though they are not visible. // Note that display:none doesn't work here. Elements with display:none don't get copied. // // Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we // wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on // everything with the tco-ellipsis class. // // As an example: The user tweets "hi http://longdomainname.com/foo" // This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo" // This will get rendered as: // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied --> // … // <!-- There's a chance the onCopy event handler might not fire. In case that happens, // we include an here so that the … doesn't bump up against the URL and ruin it. // The is inside the tco-ellipsis span so that when the onCopy handler *does* // fire, it doesn't get copied. Otherwise the copied text would have two spaces in a row, // e.g. "hi http://longdomainname.com/foo". // <span style='font-size:0'> </span> // </span> // <span style='font-size:0'> <!-- This stuff should get copied but not displayed --> // http://longdomai // </span> // <span class='js-display-url'> <!-- This stuff should get displayed *and* copied --> // nname.com/foo // </span> // <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied --> // <span style='font-size:0'> </span> // … // </span> // // Exception: pic.twitter.com images, for which expandedUrl = "https://twitter.com/#!/username/status/1234/photo/1 // For those URLs, display_url is not a substring of expanded_url, so we don't do anything special to render the elided parts. // For a pic.twitter.com URL, the only elided part will be the "https://", so this is fine. string displayURLSansEllipses = entity.DisplayUrl.Replace("…", ""); int diplayURLIndexInExpandedURL = entity.ExpandedUrl.IndexOf(displayURLSansEllipses); if (diplayURLIndexInExpandedURL != -1) { string beforeDisplayURL = entity.ExpandedUrl.Substring(0, diplayURLIndexInExpandedURL); string afterDisplayURL = entity.ExpandedUrl.Substring(diplayURLIndexInExpandedURL + displayURLSansEllipses.Length); string precedingEllipsis = entity.DisplayUrl.StartsWith("…") ? "…" : ""; string followingEllipsis = entity.DisplayUrl.EndsWith("…") ? "…" : ""; string invisibleSpan = "<span " + InvisibleTagAttrs + ">"; StringBuilder sb = new StringBuilder("<span class='tco-ellipsis'>"); sb.Append(precedingEllipsis); sb.Append(invisibleSpan).Append(" </span></span>"); sb.Append(invisibleSpan).Append(EscapeHTML(beforeDisplayURL)).Append("</span>"); sb.Append("<span class='js-display-url'>").Append(EscapeHTML(displayURLSansEllipses)).Append("</span>"); sb.Append(invisibleSpan).Append(EscapeHTML(afterDisplayURL)).Append("</span>"); sb.Append("<span class='tco-ellipsis'>").Append(invisibleSpan).Append(" </span>").Append(followingEllipsis).Append("</span>"); linkText = sb.ToString(); } else { linkText = entity.DisplayUrl; } } IDictionary <string, string> attrs = new Dictionary <string, string>(); attrs["href"] = url; if (!string.IsNullOrWhiteSpace(entity.DisplayUrl) && !string.IsNullOrWhiteSpace(entity.ExpandedUrl)) { attrs["title"] = entity.ExpandedUrl; } if (!string.IsNullOrWhiteSpace(UrlClass)) { attrs["class"] = UrlClass; } if (!string.IsNullOrWhiteSpace(UrlTarget)) { attrs["target"] = UrlTarget; } LinkToText(entity, linkText, attrs, builder); }