Example #1
0
        /// <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 || !TwitterRegex.AT_SIGNS.IsMatch(symbol);

            if (includeSymbol)
            {
                LinkToText(entity, taggedSymbol + 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 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 (TwitterRegex.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 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="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);
            }

            return(false);
        }
        /// <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 &nbsp; here so that the … doesn't bump up against the URL and ruin it.
                //        The &nbsp; 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'>&nbsp;</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'>&nbsp;</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("&nbsp;</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("&nbsp;</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);
        }