twitter-text 1.4.17 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.3
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- == twitter-text
1
+ == twitter-text {<img src="https://secure.travis-ci.org/twitter/twitter-text-rb.png" />}[http://travis-ci.org/twitter/twitter-text-rb]
2
2
 
3
3
  A gem that provides text processing routines for Twitter Tweets. The major
4
4
  reason for this is to unify the various auto-linking and extraction of
@@ -107,16 +107,6 @@ Thanks to everybody who has filed issues, provided feedback or contributed patch
107
107
 
108
108
  === Copyright and License
109
109
 
110
- Copyright 2011 Twitter, Inc.
110
+ Copyright 2011 Twitter, Inc.
111
111
 
112
- Licensed under the Apache License, Version 2.0 (the "License");
113
- you may not use this work except in compliance with the License.
114
- You may obtain a copy of the License in the LICENSE file, or at:
115
-
116
- http://www.apache.org/licenses/LICENSE-2.0
117
-
118
- Unless required by applicable law or agreed to in writing, software
119
- distributed under the License is distributed on an "AS IS" BASIS,
120
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121
- See the License for the specific language governing permissions and
122
- limitations under the License.
112
+ Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env rake
2
2
  require 'bundler'
3
+ include Rake::DSL
3
4
  Bundler::GemHelper.install_tasks
4
5
 
5
6
  task :default => ['spec', 'test:conformance']
@@ -0,0 +1,436 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'set'
4
+
5
+ module Twitter
6
+ # A module for including Tweet auto-linking in a class. The primary use of this is for helpers/views so they can auto-link
7
+ # usernames, lists, hashtags and URLs.
8
+ module Autolink extend self
9
+ # Default CSS class for auto-linked lists
10
+ DEFAULT_LIST_CLASS = "tweet-url list-slug".freeze
11
+ # Default CSS class for auto-linked usernames
12
+ DEFAULT_USERNAME_CLASS = "tweet-url username".freeze
13
+ # Default CSS class for auto-linked hashtags
14
+ DEFAULT_HASHTAG_CLASS = "tweet-url hashtag".freeze
15
+ # Default CSS class for auto-linked cashtags
16
+ DEFAULT_CASHTAG_CLASS = "tweet-url cashtag".freeze
17
+
18
+ # Default URL base for auto-linked usernames
19
+ DEFAULT_USERNAME_URL_BASE = "https://twitter.com/".freeze
20
+ # Default URL base for auto-linked lists
21
+ DEFAULT_LIST_URL_BASE = "https://twitter.com/".freeze
22
+ # Default URL base for auto-linked hashtags
23
+ DEFAULT_HASHTAG_URL_BASE = "https://twitter.com/#!/search?q=%23".freeze
24
+ # Default URL base for auto-linked cashtags
25
+ DEFAULT_CASHTAG_URL_BASE = "https://twitter.com/#!/search?q=%24".freeze
26
+
27
+ # Default attributes for invisible span tag
28
+ DEFAULT_INVISIBLE_TAG_ATTRS = "style='position:absolute;left:-9999px;'".freeze
29
+
30
+ DEFAULT_OPTIONS = {
31
+ :list_class => DEFAULT_LIST_CLASS,
32
+ :username_class => DEFAULT_USERNAME_CLASS,
33
+ :hashtag_class => DEFAULT_HASHTAG_CLASS,
34
+ :cashtag_class => DEFAULT_CASHTAG_CLASS,
35
+
36
+ :username_url_base => DEFAULT_USERNAME_URL_BASE,
37
+ :list_url_base => DEFAULT_LIST_URL_BASE,
38
+ :hashtag_url_base => DEFAULT_HASHTAG_URL_BASE,
39
+ :cashtag_url_base => DEFAULT_CASHTAG_URL_BASE,
40
+
41
+ :invisible_tag_attrs => DEFAULT_INVISIBLE_TAG_ATTRS
42
+ }.freeze
43
+
44
+ def auto_link_with_json(text, json, options = {})
45
+ # concatenate entities
46
+ entities = json.values().flatten()
47
+
48
+ # map JSON entity to twitter-text entity
49
+ entities.each do |entity|
50
+ entity.symbolize_keys!
51
+ # hashtag
52
+ entity[:hashtag] = entity[:text] if entity[:text]
53
+ end
54
+
55
+ auto_link_entities(text, entities, options)
56
+ end
57
+
58
+ def auto_link_entities(text, entities, options = {}, &block)
59
+ return text if entities.empty?
60
+
61
+ # NOTE deprecate these attributes not options keys in options hash, then use html_attrs
62
+ options = DEFAULT_OPTIONS.merge(options)
63
+ options[:html_attrs] = extract_html_attrs_from_options!(options)
64
+ options[:html_attrs][:rel] ||= "nofollow" unless options[:suppress_no_follow]
65
+
66
+ Twitter::Rewriter.rewrite_entities(text, entities) do |entity, chars|
67
+ if entity[:url]
68
+ link_to_url(entity, chars, options, &block)
69
+ elsif entity[:hashtag]
70
+ link_to_hashtag(entity, chars, options, &block)
71
+ elsif entity[:screen_name]
72
+ link_to_screen_name(entity, chars, options, &block)
73
+ elsif entity[:cashtag]
74
+ link_to_cashtag(entity, chars, options, &block)
75
+ end
76
+ end
77
+ end
78
+
79
+ # Add <tt><a></a></tt> tags around the usernames, lists, hashtags and URLs in the provided <tt>text</tt>.
80
+ # The <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt> hash:
81
+ # Also any elements in the <tt>options</tt> hash will be converted to HTML attributes
82
+ # and place in the <tt><a></tt> tag.
83
+ #
84
+ # <tt>:url_class</tt>:: class to add to url <tt><a></tt> tags
85
+ # <tt>:list_class</tt>:: class to add to list <tt><a></tt> tags
86
+ # <tt>:username_class</tt>:: class to add to username <tt><a></tt> tags
87
+ # <tt>:hashtag_class</tt>:: class to add to hashtag <tt><a></tt> tags
88
+ # <tt>:cashtag_class</tt>:: class to add to cashtag <tt><a></tt> tags
89
+ # <tt>:username_url_base</tt>:: the value for <tt>href</tt> attribute on username links. The <tt>@username</tt> (minus the <tt>@</tt>) will be appended at the end of this.
90
+ # <tt>:list_url_base</tt>:: the value for <tt>href</tt> attribute on list links. The <tt>@username/list</tt> (minus the <tt>@</tt>) will be appended at the end of this.
91
+ # <tt>:hashtag_url_base</tt>:: the value for <tt>href</tt> attribute on hashtag links. The <tt>#hashtag</tt> (minus the <tt>#</tt>) will be appended at the end of this.
92
+ # <tt>:cashtag_url_base</tt>:: the value for <tt>href</tt> attribute on cashtag links. The <tt>$cashtag</tt> (minus the <tt>$</tt>) will be appended at the end of this.
93
+ # <tt>:invisible_tag_attrs</tt>:: HTML attribute to add to invisible span tags
94
+ # <tt>:username_include_symbol</tt>:: place the <tt>@</tt> symbol within username and list links
95
+ # <tt>:suppress_lists</tt>:: disable auto-linking to lists
96
+ # <tt>:suppress_no_follow</tt>:: do not add <tt>rel="nofollow"</tt> to auto-linked items
97
+ # <tt>:symbol_tag</tt>:: tag to apply around symbol (@, #, $) in username / hashtag / cashtag links
98
+ # <tt>:text_with_symbol_tag</tt>:: tag to apply around text part in username / hashtag / cashtag links
99
+ # <tt>:url_target</tt>:: the value for <tt>target</tt> attribute on URL links.
100
+ # <tt>:link_attribute_block</tt>:: function to modify the attributes of a link based on the entity. called with |entity, attributes| params, and should modify the attributes hash.
101
+ # <tt>:link_text_block</tt>:: function to modify the text of a link based on the entity. called with |entity, text| params, and should return a modified text.
102
+ def auto_link(text, options = {}, &block)
103
+ auto_link_entities(text, Extractor.extract_entities_with_indices(text, :extract_url_without_protocol => false), options, &block)
104
+ end
105
+
106
+ # Add <tt><a></a></tt> tags around the usernames and lists in the provided <tt>text</tt>. The
107
+ # <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt> hash.
108
+ # Also any elements in the <tt>options</tt> hash will be converted to HTML attributes
109
+ # and place in the <tt><a></tt> tag.
110
+ #
111
+ # <tt>:list_class</tt>:: class to add to list <tt><a></tt> tags
112
+ # <tt>:username_class</tt>:: class to add to username <tt><a></tt> tags
113
+ # <tt>:username_url_base</tt>:: the value for <tt>href</tt> attribute on username links. The <tt>@username</tt> (minus the <tt>@</tt>) will be appended at the end of this.
114
+ # <tt>:list_url_base</tt>:: the value for <tt>href</tt> attribute on list links. The <tt>@username/list</tt> (minus the <tt>@</tt>) will be appended at the end of this.
115
+ # <tt>:username_include_symbol</tt>:: place the <tt>@</tt> symbol within username and list links
116
+ # <tt>:suppress_lists</tt>:: disable auto-linking to lists
117
+ # <tt>:suppress_no_follow</tt>:: do not add <tt>rel="nofollow"</tt> to auto-linked items
118
+ # <tt>:symbol_tag</tt>:: tag to apply around symbol (@, #, $) in username / hashtag / cashtag links
119
+ # <tt>:text_with_symbol_tag</tt>:: tag to apply around text part in username / hashtag / cashtag links
120
+ # <tt>:link_attribute_block</tt>:: function to modify the attributes of a link based on the entity. called with |entity, attributes| params, and should modify the attributes hash.
121
+ # <tt>:link_text_block</tt>:: function to modify the text of a link based on the entity. called with |entity, text| params, and should return a modified text.
122
+ def auto_link_usernames_or_lists(text, options = {}, &block) # :yields: list_or_username
123
+ auto_link_entities(text, Extractor.extract_mentions_or_lists_with_indices(text), options, &block)
124
+ end
125
+
126
+ # Add <tt><a></a></tt> tags around the hashtags in the provided <tt>text</tt>.
127
+ # The <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt> hash.
128
+ # Also any elements in the <tt>options</tt> hash will be converted to HTML attributes
129
+ # and place in the <tt><a></tt> tag.
130
+ #
131
+ # <tt>:hashtag_class</tt>:: class to add to hashtag <tt><a></tt> tags
132
+ # <tt>:hashtag_url_base</tt>:: the value for <tt>href</tt> attribute. The hashtag text (minus the <tt>#</tt>) will be appended at the end of this.
133
+ # <tt>:suppress_no_follow</tt>:: do not add <tt>rel="nofollow"</tt> to auto-linked items
134
+ # <tt>:symbol_tag</tt>:: tag to apply around symbol (@, #, $) in username / hashtag / cashtag links
135
+ # <tt>:text_with_symbol_tag</tt>:: tag to apply around text part in username / hashtag / cashtag links
136
+ # <tt>:link_attribute_block</tt>:: function to modify the attributes of a link based on the entity. called with |entity, attributes| params, and should modify the attributes hash.
137
+ # <tt>:link_text_block</tt>:: function to modify the text of a link based on the entity. called with |entity, text| params, and should return a modified text.
138
+ def auto_link_hashtags(text, options = {}, &block) # :yields: hashtag_text
139
+ auto_link_entities(text, Extractor.extract_hashtags_with_indices(text), options, &block)
140
+ end
141
+
142
+ # Add <tt><a></a></tt> tags around the cashtags in the provided <tt>text</tt>.
143
+ # The <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt> hash.
144
+ # Also any elements in the <tt>options</tt> hash will be converted to HTML attributes
145
+ # and place in the <tt><a></tt> tag.
146
+ #
147
+ # <tt>:cashtag_class</tt>:: class to add to cashtag <tt><a></tt> tags
148
+ # <tt>:cashtag_url_base</tt>:: the value for <tt>href</tt> attribute. The cashtag text (minus the <tt>$</tt>) will be appended at the end of this.
149
+ # <tt>:suppress_no_follow</tt>:: do not add <tt>rel="nofollow"</tt> to auto-linked items
150
+ # <tt>:symbol_tag</tt>:: tag to apply around symbol (@, #, $) in username / hashtag / cashtag links
151
+ # <tt>:text_with_symbol_tag</tt>:: tag to apply around text part in username / hashtag / cashtag links
152
+ # <tt>:link_attribute_block</tt>:: function to modify the attributes of a link based on the entity. called with |entity, attributes| params, and should modify the attributes hash.
153
+ # <tt>:link_text_block</tt>:: function to modify the text of a link based on the entity. called with |entity, text| params, and should return a modified text.
154
+ def auto_link_cashtags(text, options = {}, &block) # :yields: cashtag_text
155
+ auto_link_entities(text, Extractor.extract_cashtags_with_indices(text), options, &block)
156
+ end
157
+
158
+ # Add <tt><a></a></tt> tags around the URLs in the provided <tt>text</tt>.
159
+ # The <tt><a></tt> tags can be controlled with the following entries in the <tt>options</tt> hash.
160
+ # Also any elements in the <tt>options</tt> hash will be converted to HTML attributes
161
+ # and place in the <tt><a></tt> tag.
162
+ #
163
+ # <tt>:url_class</tt>:: class to add to url <tt><a></tt> tags
164
+ # <tt>:invisible_tag_attrs</tt>:: HTML attribute to add to invisible span tags
165
+ # <tt>:suppress_no_follow</tt>:: do not add <tt>rel="nofollow"</tt> to auto-linked items
166
+ # <tt>:symbol_tag</tt>:: tag to apply around symbol (@, #, $) in username / hashtag / cashtag links
167
+ # <tt>:text_with_symbol_tag</tt>:: tag to apply around text part in username / hashtag / cashtag links
168
+ # <tt>:url_target</tt>:: the value for <tt>target</tt> attribute on URL links.
169
+ # <tt>:link_attribute_block</tt>:: function to modify the attributes of a link based on the entity. called with |entity, attributes| params, and should modify the attributes hash.
170
+ # <tt>:link_text_block</tt>:: function to modify the text of a link based on the entity. called with |entity, text| params, and should return a modified text.
171
+ def auto_link_urls(text, options = {}, &block)
172
+ auto_link_entities(text, Extractor.extract_urls_with_indices(text, :extract_url_without_protocol => false), options, &block)
173
+ end
174
+
175
+ # These methods are deprecated, will be removed in future.
176
+ extend Deprecation
177
+
178
+ # <b>Deprecated</b>: Please use auto_link_urls instead.
179
+ # Add <tt><a></a></tt> tags around the URLs in the provided <tt>text</tt>.
180
+ # Any elements in the <tt>href_options</tt> hash will be converted to HTML attributes
181
+ # and place in the <tt><a></tt> tag.
182
+ # Unless <tt>href_options</tt> contains <tt>:suppress_no_follow</tt>
183
+ # the <tt>rel="nofollow"</tt> attribute will be added.
184
+ alias :auto_link_urls_custom :auto_link_urls
185
+ deprecate :auto_link_urls_custom, :auto_link_urls
186
+
187
+ private
188
+
189
+ HTML_ENTITIES = {
190
+ '&' => '&amp;',
191
+ '>' => '&gt;',
192
+ '<' => '&lt;',
193
+ '"' => '&quot;',
194
+ "'" => '&#39;'
195
+ }
196
+
197
+ def html_escape(text)
198
+ text && text.to_s.gsub(/[&"'><]/) do |character|
199
+ HTML_ENTITIES[character]
200
+ end
201
+ end
202
+
203
+ # NOTE We will make this private in future.
204
+ public :html_escape
205
+
206
+ # Options which should not be passed as HTML attributes
207
+ OPTIONS_NOT_ATTRIBUTES = Set.new([
208
+ :url_class, :list_class, :username_class, :hashtag_class, :cashtag_class,
209
+ :username_url_base, :list_url_base, :hashtag_url_base, :cashtag_url_base,
210
+ :username_url_block, :list_url_block, :hashtag_url_block, :link_url_block,
211
+ :username_include_symbol, :suppress_lists, :suppress_no_follow, :url_entities,
212
+ :invisible_tag_attrs, :symbol_tag, :text_with_symbol_tag, :url_target,
213
+ :link_attribute_block, :link_text_block
214
+ ]).freeze
215
+
216
+ def extract_html_attrs_from_options!(options)
217
+ html_attrs = {}
218
+ options.reject! do |key, value|
219
+ unless OPTIONS_NOT_ATTRIBUTES.include?(key)
220
+ html_attrs[key] = value
221
+ true
222
+ end
223
+ end
224
+ html_attrs
225
+ end
226
+
227
+ def url_entities_hash(url_entities)
228
+ (url_entities || {}).inject({}) do |entities, entity|
229
+ entity = entity.symbolize_keys
230
+ entities[entity[:url]] = entity
231
+ entities
232
+ end
233
+ end
234
+
235
+ def link_to_url(entity, chars, options = {})
236
+ url = entity[:url]
237
+
238
+ href = if options[:link_url_block]
239
+ options[:link_url_block].call(url)
240
+ else
241
+ url
242
+ end
243
+
244
+ # NOTE auto link to urls do not use any default values and options
245
+ # like url_class but use suppress_no_follow.
246
+ html_attrs = options[:html_attrs].dup
247
+ html_attrs[:class] = options[:url_class] if options.key?(:url_class)
248
+
249
+ # add target attribute only if :url_target is specified
250
+ html_attrs[:target] = options[:url_target] if options.key?(:url_target)
251
+
252
+ url_entities = url_entities_hash(options[:url_entities])
253
+
254
+ # use entity from urlEntities if available
255
+ url_entity = url_entities[url] || entity
256
+ link_text = if url_entity[:display_url]
257
+ html_attrs[:title] ||= url_entity[:expanded_url]
258
+ link_url_with_entity(url_entity, options)
259
+ else
260
+ html_escape(url)
261
+ end
262
+
263
+ link_to_text(entity, link_text, href, html_attrs, options)
264
+ end
265
+
266
+ def link_url_with_entity(entity, options)
267
+ display_url = entity[:display_url]
268
+ expanded_url = entity[:expanded_url]
269
+ invisible_tag_attrs = options[:invisible_tag_attrs] || DEFAULT_INVISIBLE_TAG_ATTRS
270
+
271
+ # Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste
272
+ # should contain the full original URL (expanded_url), not the display URL.
273
+ #
274
+ # Method: Whenever possible, we actually emit HTML that contains expanded_url, and use
275
+ # font-size:0 to hide those parts that should not be displayed (because they are not part of display_url).
276
+ # Elements with font-size:0 get copied even though they are not visible.
277
+ # Note that display:none doesn't work here. Elements with display:none don't get copied.
278
+ #
279
+ # Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we
280
+ # wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on
281
+ # everything with the tco-ellipsis class.
282
+ #
283
+ # Exception: pic.twitter.com images, for which expandedUrl = "https://twitter.com/#!/username/status/1234/photo/1
284
+ # For those URLs, display_url is not a substring of expanded_url, so we don't do anything special to render the elided parts.
285
+ # For a pic.twitter.com URL, the only elided part will be the "https://", so this is fine.
286
+ display_url_sans_ellipses = display_url.gsub("…", "")
287
+
288
+ if expanded_url.include?(display_url_sans_ellipses)
289
+ before_display_url, after_display_url = expanded_url.split(display_url_sans_ellipses, 2)
290
+ preceding_ellipsis = /\A…/.match(display_url).to_s
291
+ following_ellipsis = /…\z/.match(display_url).to_s
292
+
293
+ # As an example: The user tweets "hi http://longdomainname.com/foo"
294
+ # This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo"
295
+ # This will get rendered as:
296
+ # <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
297
+ # …
298
+ # <!-- There's a chance the onCopy event handler might not fire. In case that happens,
299
+ # we include an &nbsp; here so that the … doesn't bump up against the URL and ruin it.
300
+ # The &nbsp; is inside the tco-ellipsis span so that when the onCopy handler *does*
301
+ # fire, it doesn't get copied. Otherwise the copied text would have two spaces in a row,
302
+ # e.g. "hi http://longdomainname.com/foo".
303
+ # <span style='font-size:0'>&nbsp;</span>
304
+ # </span>
305
+ # <span style='font-size:0'> <!-- This stuff should get copied but not displayed -->
306
+ # http://longdomai
307
+ # </span>
308
+ # <span class='js-display-url'> <!-- This stuff should get displayed *and* copied -->
309
+ # nname.com/foo
310
+ # </span>
311
+ # <span class='tco-ellipsis'> <!-- This stuff should get displayed but not copied -->
312
+ # <span style='font-size:0'>&nbsp;</span>
313
+ # …
314
+ # </span>
315
+ %(<span class="tco-ellipsis">#{preceding_ellipsis}<span #{invisible_tag_attrs}>&nbsp;</span></span>) <<
316
+ %(<span #{invisible_tag_attrs}>#{html_escape(before_display_url)}</span>) <<
317
+ %(<span class="js-display-url">#{html_escape(display_url_sans_ellipses)}</span>) <<
318
+ %(<span #{invisible_tag_attrs}>#{html_escape(after_display_url)}</span>) <<
319
+ %(<span class="tco-ellipsis"><span #{invisible_tag_attrs}>&nbsp;</span>#{following_ellipsis}</span>)
320
+ else
321
+ html_escape(display_url)
322
+ end
323
+ end
324
+
325
+ def link_to_hashtag(entity, chars, options = {})
326
+ hash = chars[entity[:indices].first]
327
+ hashtag = entity[:hashtag]
328
+ hashtag = yield(hashtag) if block_given?
329
+
330
+ href = if options[:hashtag_url_block]
331
+ options[:hashtag_url_block].call(hashtag)
332
+ else
333
+ "#{options[:hashtag_url_base]}#{hashtag}"
334
+ end
335
+
336
+ html_attrs = {
337
+ :class => "#{options[:hashtag_class]}",
338
+ # FIXME As our conformance test, hash in title should be half-width,
339
+ # this should be bug of conformance data.
340
+ :title => "##{hashtag}"
341
+ }.merge(options[:html_attrs])
342
+
343
+ link_to_text_with_symbol(entity, hash, hashtag, href, html_attrs, options)
344
+ end
345
+
346
+ def link_to_cashtag(entity, chars, options = {})
347
+ dollar = chars[entity[:indices].first]
348
+ cashtag = entity[:cashtag]
349
+ cashtag = yield(cashtag) if block_given?
350
+
351
+ href = if options[:cashtag_url_block]
352
+ options[:cashtag_url_block].call(cashtag)
353
+ else
354
+ "#{options[:cashtag_url_base]}#{cashtag}"
355
+ end
356
+
357
+ html_attrs = {
358
+ :class => "#{options[:cashtag_class]}",
359
+ :title => "$#{cashtag}"
360
+ }.merge(options[:html_attrs])
361
+
362
+ link_to_text_with_symbol(entity, dollar, cashtag, href, html_attrs, options)
363
+ end
364
+
365
+ def link_to_screen_name(entity, chars, options = {})
366
+ name = "#{entity[:screen_name]}#{entity[:list_slug]}"
367
+ chunk = name
368
+ chunk = yield(name) if block_given?
369
+ name.downcase!
370
+
371
+ at = chars[entity[:indices].first]
372
+
373
+ html_attrs = options[:html_attrs].dup
374
+
375
+ if entity[:list_slug] && !entity[:list_slug].empty? && !options[:suppress_lists]
376
+ href = if options[:list_url_block]
377
+ options[:list_url_block].call(name)
378
+ else
379
+ "#{options[:list_url_base]}#{name}"
380
+ end
381
+ html_attrs[:class] ||= "#{options[:list_class]}"
382
+ else
383
+ href = if options[:username_url_block]
384
+ options[:username_url_block].call(chunk)
385
+ else
386
+ "#{options[:username_url_base]}#{name}"
387
+ end
388
+ html_attrs[:class] ||= "#{options[:username_class]}"
389
+ end
390
+
391
+ link_to_text_with_symbol(entity, at, chunk, href, html_attrs, options)
392
+ end
393
+
394
+ def link_to_text_with_symbol(entity, symbol, text, href, attributes = {}, options = {})
395
+ tagged_symbol = options[:symbol_tag] ? "<#{options[:symbol_tag]}>#{symbol}</#{options[:symbol_tag]}>" : symbol
396
+ text = html_escape(text)
397
+ tagged_text = options[:text_with_symbol_tag] ? "<#{options[:text_with_symbol_tag]}>#{text}</#{options[:text_with_symbol_tag]}>" : text
398
+ if options[:username_include_symbol] || symbol !~ Twitter::Regex::REGEXEN[:at_signs]
399
+ "#{link_to_text(entity, tagged_symbol + tagged_text, href, attributes, options)}"
400
+ else
401
+ "#{tagged_symbol}#{link_to_text(entity, tagged_text, href, attributes, options)}"
402
+ end
403
+ end
404
+
405
+ def link_to_text(entity, text, href, attributes = {}, options = {})
406
+ attributes[:href] = href
407
+ options[:link_attribute_block].call(entity, attributes) if options[:link_attribute_block]
408
+ text = options[:link_text_block].call(entity, text) if options[:link_text_block]
409
+ %(<a#{tag_attrs(attributes)}>#{text}</a>)
410
+ end
411
+
412
+ BOOLEAN_ATTRIBUTES = Set.new([:disabled, :readonly, :multiple, :checked]).freeze
413
+
414
+ def tag_attrs(attributes)
415
+ attributes.keys.sort_by{|k| k.to_s}.inject("") do |attrs, key|
416
+ value = attributes[key]
417
+
418
+ if BOOLEAN_ATTRIBUTES.include?(key)
419
+ value = value ? key : nil
420
+ end
421
+
422
+ unless value.nil?
423
+ value = case value
424
+ when Array
425
+ value.compact.join(" ")
426
+ else
427
+ value
428
+ end
429
+ attrs << %( #{html_escape(key)}="#{html_escape(value)}")
430
+ end
431
+
432
+ attrs
433
+ end
434
+ end
435
+ end
436
+ end
@@ -0,0 +1,15 @@
1
+ module Twitter
2
+ module Deprecation
3
+ def deprecate(method, new_method = nil)
4
+ deprecated_method = :"deprecated_#{method}"
5
+ message = "Deprecation: `#{method}` is deprecated."
6
+ message << " Please use `#{new_method}` instead." if new_method
7
+
8
+ alias_method(deprecated_method, method)
9
+ define_method method do |*args, &block|
10
+ warn message
11
+ send(deprecated_method, *args, &block)
12
+ end
13
+ end
14
+ end
15
+ end