searchlink 2.3.85 → 2.3.86

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02b111da9ce13f8646d7357ae5560a1c6b84bb45fd506dca8fafe96e1c844bea
4
- data.tar.gz: 02fd69545df789fdf1105dea4971b53f02bfc33ac0da20a3f25454c11f9a9ae7
3
+ metadata.gz: c00e022d8b8155db4e6fd3acd6ccaa69be955a316774be41747cce0e476f0df6
4
+ data.tar.gz: 8487fcec9ab7d7830c467e5bfa2ed4008104a2363cb776b18864848891a7d98b
5
5
  SHA512:
6
- metadata.gz: 7af24cad60ca01ead8ab4a76f82c6d6fa9be6aa30752f088c23cfe387d9a3adf8f076e5e4d1f1dc25d9eeda8879d1ae7e83bbee3bcea250de6914dc67d35cf0c
7
- data.tar.gz: '089fb329c4c7be4cf30bf929b8c4309374a0b8055f42eb2adeb2c62d29f7a9ef84d9e45527a5c0a13c7e0061345ee814cfd431bc6f33614ad034f8d8c970250a'
6
+ metadata.gz: 66ee6b30d332f4047f4f1528a8f39b6eb51cbb0b6ef0f3abd5b546ea3dd4433ec212c4b72c9d46a680c65e69078afc617caf8d5d7da757181281bb5c16518ad8
7
+ data.tar.gz: 7b29d19cdddbc9c6ecbe07ecaf7cc23e685da82fd03fe16645a4775ee0aa15ca96bc859da6bc38725fdd074642fa986a396bd745e4af87379bfbfd17f60e83b2
@@ -82,6 +82,11 @@ module SL
82
82
  # E.g. [](!g Search Text)
83
83
  empty_uses_page_title: false
84
84
 
85
+ # If confirm is true, then a popup dialog will be displayed
86
+ # showing the destination of each found link. Hitting cancel
87
+ # will leave the link unchanged.
88
+ confirm: false
89
+
85
90
  # To create custom abbreviations for Google Site Searches,
86
91
  # add to (or replace) the hash below.
87
92
  # "abbreviation" => "site.url",
@@ -168,6 +173,9 @@ module SL
168
173
  # amazon_partner: "bretttercom-20"
169
174
  config["amazon_partner"] ||= ""
170
175
 
176
+ # display a popup dialog confirmation
177
+ config["confirm"] ||= false
178
+
171
179
  # To create custom abbreviations for Google Site Searches,
172
180
  # add to (or replace) the hash below.
173
181
  # "abbreviation" => "site.url",
@@ -176,7 +184,7 @@ module SL
176
184
  # hash can override existing search triggers.
177
185
  config["custom_site_searches"] ||= {
178
186
  "bt" => "brettterpstra.com",
179
- "imdb" => "imdb.com"
187
+ "imdb" => "imdb.com",
180
188
  }
181
189
 
182
190
  # confirm existence of links generated from custom search replacements
@@ -90,7 +90,7 @@ module Curl
90
90
  tag: tag,
91
91
  source: tag_source,
92
92
  attrs: attrs,
93
- content: contents
93
+ content: contents,
94
94
  }
95
95
  end
96
96
 
@@ -147,7 +147,7 @@ module Curl
147
147
  output << {
148
148
  type: "opengraph",
149
149
  attrs: nil,
150
- src: @meta[src]
150
+ src: @meta[src],
151
151
  }
152
152
  end
153
153
  images = tags(%w[img source])
@@ -162,21 +162,21 @@ module Curl
162
162
  image, media = s.split(/ /)
163
163
  srcset << {
164
164
  src: image,
165
- media: media
165
+ media: media,
166
166
  }
167
167
  end
168
168
  end
169
169
  output << {
170
170
  type: "srcset",
171
171
  attrs: img[:attrs],
172
- images: srcset
172
+ images: srcset,
173
173
  }
174
174
  end
175
175
  when /img/
176
176
  output << {
177
177
  type: "img",
178
178
  src: img[:attrs].filter { |a| a[:key] =~ /src/i }.first[:value],
179
- attrs: img[:attrs]
179
+ attrs: img[:attrs],
180
180
  }
181
181
  end
182
182
  end
@@ -189,7 +189,7 @@ module Curl
189
189
  links = @links.nil? ? 0 : @links.count
190
190
  [
191
191
  %(<HTMLCurl: @code="#{@code}" @url="#{@url}" @title="#{@title}"),
192
- %(@description=#{@description} @headers:#{headers} @meta:#{meta} @links:#{links}>)
192
+ %(@description=#{@description} @headers:#{headers} @meta:#{meta} @links:#{links}>),
193
193
  ].join(" ")
194
194
  end
195
195
 
@@ -253,14 +253,22 @@ module Curl
253
253
  attrs = tag["attrs"].strip.to_enum(:scan, /(?ix)
254
254
  (?<key>[@a-z0-9-]+)(?:=(?<quot>["'])
255
255
  (?<value>[^"']+)\k<quot>|[ >])?/i).map { Regexp.last_match }
256
- attrs.map! { |a| { key: a["key"], value: a["key"] =~ /^(class|rel)$/ ? a["value"].split(/ /) : a["value"] } }
256
+
257
+ attrs.map! do |a|
258
+ val = if a["key"] =~ /^(class|rel)$/
259
+ a["value"].nil? ? "" : a["value"].split(/ /)
260
+ else
261
+ a["value"]
262
+ end
263
+ { key: a["key"], value: val }
264
+ end
257
265
  end
258
266
  {
259
267
  tag: tag["tag"],
260
268
  source: tag.to_s,
261
269
  attrs: attrs,
262
270
  content: tag["content"],
263
- tags: content_tags(tag["content"])
271
+ tags: content_tags(tag["content"]),
264
272
  }
265
273
  end
266
274
  end
@@ -351,7 +359,7 @@ module Curl
351
359
  title: title,
352
360
  rel: rel,
353
361
  text: text,
354
- class: link_class
362
+ class: link_class,
355
363
  }
356
364
  links << link
357
365
  end
@@ -115,16 +115,7 @@ module SL
115
115
 
116
116
  url.add_query_string!
117
117
 
118
- url = case SL.shortener
119
- when :isgd
120
- SL::IsgdSearch.shorten(url)
121
- when :tinyurl
122
- SL::TinyurlSearch.shorten(url)
123
- when :bitly
124
- SL::BitlySearch.shorten(url)
125
- else
126
- url
127
- end
118
+ url = SL::Shortener.shorten(url, SL.shortener)
128
119
 
129
120
  case type.to_sym
130
121
  when :ref_title
@@ -257,10 +248,10 @@ module SL
257
248
 
258
249
  out = ""
259
250
  inline = if SL.originput.split(/\n/).length > 1
260
- false
261
- else
262
- SL.config["inline"] || SL.originput.split(/\n/).length == 1
263
- end
251
+ false
252
+ else
253
+ SL.config["inline"] || SL.originput.split(/\n/).length == 1
254
+ end
264
255
 
265
256
  SL.errors.each do |k, v|
266
257
  next if v.empty?
@@ -268,10 +259,10 @@ module SL
268
259
  v.each_with_index do |err, i|
269
260
  out += "(#{k}) #{err}"
270
261
  out += if inline
271
- i == v.length - 1 ? " | " : ", "
272
- else
273
- "\n"
274
- end
262
+ i == v.length - 1 ? " | " : ", "
263
+ else
264
+ "\n"
265
+ end
275
266
  end
276
267
  end
277
268
 
@@ -2,6 +2,14 @@
2
2
 
3
3
  module SL
4
4
  class SearchLink
5
+ # Confirm a URL with a popup if requested
6
+ def confirmed?(url)
7
+ return true unless SL.config["confirm"]
8
+
9
+ SL::Shortener.confirm?(url)
10
+ end
11
+
12
+ # Parse the input string and perform searches
5
13
  def parse(input)
6
14
  SL.output = []
7
15
  return false if input.empty?
@@ -283,6 +291,13 @@ module SL
283
291
  end
284
292
 
285
293
  if @url
294
+ res = confirmed?(@url)
295
+ if !res
296
+ return match
297
+ else
298
+ @url = res if res.is_a?(String) && SL::URL.url?(res)
299
+ end
300
+
286
301
  title = SL::URL.title(@url) if SL.titleize && title == ""
287
302
 
288
303
  @link_text = title if @link_text == "" && title
@@ -473,16 +488,28 @@ module SL
473
488
  end
474
489
 
475
490
  if @url
476
- if type =~ /sp(ell)?/
477
- SL.add_output(@url)
478
- elsif link_only
479
- SL.add_output(@url)
480
- elsif @url == "embed"
481
- SL.add_output(title)
482
- else
483
- type = reference_link ? :ref_title : :inline
491
+ res = confirmed?(@url)
492
+ if res
493
+ if res.is_a?(String) && SL::URL.url?(res)
494
+ @url = res
495
+ title = SL::URL.title(@url) unless title == ""
496
+ end
497
+
498
+ if type =~ /sp(ell)?/
499
+ SL.add_output(@url)
500
+ elsif link_only
501
+ SL.add_output(@url)
502
+ elsif @url == "embed"
503
+ SL.add_output(title)
504
+ else
505
+ type = reference_link ? :ref_title : :inline
484
506
 
485
- SL.add_output SL.make_link(type, @link_text, @url, title: title, force_title: false)
507
+ SL.add_output SL.make_link(type, @link_text, @url, title: title, force_title: false)
508
+ SL.print_errors
509
+ end
510
+ else
511
+ SL.add_error("Canceled", "User canceled result #{@url}")
512
+ SL.add_output SL.originput.chomp
486
513
  SL.print_errors
487
514
  end
488
515
  else
@@ -555,7 +582,7 @@ module SL
555
582
 
556
583
  input.parse_flags! unless skip_flags
557
584
 
558
- options = %w[debug country_code inline prefix_random include_titles remove_seo validate_links complete_bare]
585
+ options = %w[debug country_code inline prefix_random include_titles remove_seo validate_links complete_bare confirm]
559
586
  options.each do |o|
560
587
  if input =~ /^ *#{o}:\s+(\S+)$/
561
588
  val = Regexp.last_match(1).strip
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SL
4
+ class PopupSearch
5
+ class << self
6
+ def settings
7
+ {
8
+ trigger: "pop(up)?[gaw]?",
9
+ searches: [
10
+ ["pop", "Popup DuckDuckGo Search"],
11
+ ["popa", "Popup Amazon Search"],
12
+ ["popg", "Popup Google Search"],
13
+ ["popw", "Popup Wikipedia Search"]
14
+ ],
15
+ }
16
+ end
17
+
18
+ def search(type, search_terms, link_text)
19
+ return [false, false, link_text] unless workflow_exist?
20
+
21
+ term = search_terms.url_encode
22
+ url = case type
23
+ when /g$/
24
+ "https://www.google.com/search?hl=en&q=#{term}"
25
+ when /a$/
26
+ "https://www.amazon.com/s?k=#{term}"
27
+ when /b$/
28
+ "https://www.bing.com/search?q=#{term}"
29
+ when /w$/
30
+ "https://en.wikipedia.org/w/index.php?search=#{term}&title=Special%3ASearch&ns0=1"
31
+ else
32
+ "https://duckduckgo.com/?q=#{term}&ia=web"
33
+ end
34
+
35
+ path = File.expand_path("~/Library/Services/Preview URL.workflow")
36
+ res = `automator -i "#{url}" "#{path}"`.strip
37
+
38
+ begin
39
+ if res.empty?
40
+ SL.add_error("Canceled", "Popup Search Cancelled")
41
+ return [false, false, link_text]
42
+ end
43
+
44
+ title = SL::URL.title(res)
45
+
46
+ link_text = title if link_text == "" && !SL.titleize
47
+
48
+ [res, title, link_text]
49
+ rescue StandardError
50
+ false
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def workflow_exist?
57
+ unless File.exist?(File.expand_path("~/Library/Services/Preview URL.workflow"))
58
+ SL.add_error("Missing Service", "Preview URL Service not installed")
59
+ return false
60
+ end
61
+
62
+ true
63
+ end
64
+ end
65
+
66
+ SL::Searches.register "popup", :search, self
67
+ end
68
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # import
4
+ require_relative "shorteners/bitly"
5
+
6
+ # import
7
+ require_relative "shorteners/isgd"
8
+
9
+ # import
10
+ require_relative "shorteners/tinyurl"
11
+
12
+ module SL
13
+ class Shortener
14
+ class << self
15
+ # Displays an AppleScript dialog to confirm shortening a URL.
16
+ #
17
+ # @param url [String] The URL to confirm shortening for.
18
+ # @return [Boolean] True if the user confirms, false otherwise.
19
+ #
20
+ def confirm?(url, title: "Confirm URL?")
21
+ if File.exist?(File.expand_path("~/Library/Services/Preview URL.workflow"))
22
+ cmd = %(osascript -e "display dialog \\"#{url}\\" with title \\"#{title}\\" buttons {\\"Cancel\\", \\"Confirm\\", \\"Preview\\"}")
23
+
24
+ res = `#{cmd}`.strip
25
+
26
+ if res =~ /Preview/
27
+ path = File.expand_path("~/Library/Services/Preview URL.workflow")
28
+ res = `automator -i "#{url}" "#{path}"`.strip
29
+
30
+ return res.empty? ? false : res
31
+ else
32
+ return res =~ /Confirm/
33
+ end
34
+ end
35
+
36
+ res = system(%(osascript -e "display dialog \"#{url}\" with title \"#{title}\" buttons {\"Cancel\", \"Confirm\"}"))
37
+
38
+ res == 0
39
+ end
40
+
41
+ # Shortens a URL using the specified shortener.
42
+ #
43
+ # @param url [String] The URL to shorten.
44
+ # @param shortener [Symbol] The shortener to use (:tinyurl, :bitly, :isgd).
45
+ # @return [String] The shortened URL.
46
+ # @raise [ArgumentError] If the shortener is unknown.
47
+ #
48
+ # @example
49
+ # SL::Shortener.shorten('http://example.com', :tinyurl)
50
+ #
51
+ def shorten(url, shortener)
52
+ # Check if the URL is already shortened
53
+ return url unless SL::URL.url?(url)
54
+
55
+ known_shorteners = %i[tinyurl bitly isgd]
56
+ return url unless known_shorteners.include?(shortener)
57
+
58
+ # Confirm shortening the URL
59
+ res = SL::Shortener.confirm?(url)
60
+
61
+ return url unless res
62
+
63
+ url = res if res.is_a?(String)
64
+
65
+ return url unless SL::URL.url?(url)
66
+
67
+ # Use the shortener to shorten the URL
68
+ case shortener
69
+ when :tinyurl
70
+ SL::TinyurlSearch.shorten(url)
71
+ when :bitly
72
+ SL::BitlySearch.shorten(url)
73
+ when :isgd
74
+ SL::IsgdSearch.shorten(url)
75
+ else
76
+ raise ArgumentError, "Unknown shortener"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -43,9 +43,9 @@ module SL
43
43
  #
44
44
  def available_searches_html
45
45
  searches = plugins[:search]
46
- .flat_map { |_, plugin| plugin[:searches] }
47
- .reject { |s| s[1].nil? }
48
- .sort_by { |s| s[0].is_a?(Array) ? s[0][0] : s[0] }
46
+ .flat_map { |_, plugin| plugin[:searches] }
47
+ .reject { |s| s[1].nil? }
48
+ .sort_by { |s| s[0].is_a?(Array) ? s[0][0] : s[0] }
49
49
  out = ['<table id="searches">',
50
50
  "<thead><td>Shortcut</td><td>Search Type</td></thead>",
51
51
  "<tbody>"]
@@ -53,7 +53,7 @@ module SL
53
53
  searches.each do |s|
54
54
  out << "<tr>
55
55
  <td>
56
- <code>!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..].join(',')})" : s[0]}
56
+ <code>!#{s[0].is_a?(Array) ? "#{s[0][0]} (#{s[0][1..].join(",")})" : s[0]}
57
57
  </code>
58
58
  </td><td>#{s[1]}</td></tr>"
59
59
  end
@@ -72,10 +72,10 @@ module SL
72
72
 
73
73
  searches.each do |s|
74
74
  shortcut = if s[0].is_a?(Array)
75
- "#{s[0][0]} (#{s[0][1..].join(',')})"
76
- else
77
- s[0]
78
- end
75
+ "#{s[0][0]} (#{s[0][1..].join(",")})"
76
+ else
77
+ s[0]
78
+ end
79
79
 
80
80
  out << "!#{shortcut}#{shortcut.spacer}#{s[1]}"
81
81
  end
@@ -95,7 +95,7 @@ module SL
95
95
 
96
96
  def did_you_mean(term)
97
97
  matches = best_search_match(term)
98
- matches.empty? ? "" : ", did you mean #{matches.map { |m| "!#{m}" }.join(', ')}?"
98
+ matches.empty? ? "" : ", did you mean #{matches.map { |m| "!#{m}" }.join(", ")}?"
99
99
  end
100
100
 
101
101
  def valid_searches
@@ -106,7 +106,7 @@ module SL
106
106
 
107
107
  def valid_search?(term)
108
108
  valid = false
109
- valid = true if term =~ /^(#{valid_searches.join('|')})$/
109
+ valid = true if term =~ /^(#{valid_searches.join("|")})$/
110
110
  valid = true if SL.config["custom_site_searches"].keys.include? term
111
111
  # SL.notify("Invalid search#{did_you_mean(term)}", term) unless valid
112
112
  valid
@@ -124,7 +124,7 @@ module SL
124
124
  trigger: settings.fetch(:trigger, title).normalize_trigger,
125
125
  searches: settings[:searches],
126
126
  config: settings[:config],
127
- class: klass
127
+ class: klass,
128
128
  }
129
129
  end
130
130
 
@@ -155,11 +155,11 @@ module SL
155
155
  config = IO.read(file)
156
156
 
157
157
  cfg = case ext
158
- when /^y/i
159
- YAML.safe_load(config)
160
- else
161
- JSON.parse(config)
162
- end
158
+ when /^y/i
159
+ YAML.safe_load(config)
160
+ else
161
+ JSON.parse(config)
162
+ end
163
163
  cfg["filename"] = File.basename(file)
164
164
  cfg["path"] = file.shorten_path
165
165
  SL::ScriptSearch.new(cfg)
@@ -169,10 +169,12 @@ module SL
169
169
  def do_search(search_type, search_terms, link_text, timeout: SL.config["timeout"])
170
170
  plugins[:search].each_value do |plugin|
171
171
  trigger = plugin[:trigger].gsub(/(^\^|\$$)/, "")
172
- if search_type =~ /^#{trigger}$/
173
- search = proc { plugin[:class].search(search_type, search_terms, link_text) }
174
- return SL::Util.search_with_timeout(search, timeout)
175
- end
172
+ next unless search_type =~ /^#{trigger}$/
173
+
174
+ search = proc { plugin[:class].search(search_type, search_terms, link_text) }
175
+ SL.config["skip_timeout"] = true if trigger =~ /^pop/
176
+
177
+ return SL::Util.search_with_timeout(search, timeout)
176
178
  end
177
179
  end
178
180
  end
@@ -188,8 +190,8 @@ require_relative "searches/itunes"
188
190
  # import
189
191
  require_relative "searches/amazon"
190
192
 
191
- # import
192
- require_relative "searches/bitly"
193
+ #import
194
+ require_relative "searches/shortener"
193
195
 
194
196
  # import
195
197
  require_relative "searches/definition"
@@ -209,15 +211,15 @@ require_relative "searches/history"
209
211
  # import
210
212
  require_relative "searches/hook"
211
213
 
212
- # import
213
- require_relative "searches/isgd"
214
-
215
214
  # import
216
215
  require_relative "searches/lastfm"
217
216
 
218
217
  # import
219
218
  require_relative "searches/pinboard"
220
219
 
220
+ # import
221
+ require_relative "searches/popup"
222
+
221
223
  # import
222
224
  require_relative "searches/setapp"
223
225
 
@@ -248,8 +250,5 @@ require_relative "searches/youtube"
248
250
  # import
249
251
  require_relative "searches/stackoverflow"
250
252
 
251
- # import
252
- require_relative "searches/tinyurl"
253
-
254
253
  # import
255
254
  require_relative "searches/linkding"
@@ -59,15 +59,15 @@ module SL
59
59
 
60
60
  shortener = split(/_/).last
61
61
  SL.shortener = case shortener
62
- when /i/i
63
- :isgd
64
- when /b/i
65
- :bitly
66
- when /t/i
67
- :tinyurl
68
- else
69
- :none
70
- end
62
+ when /i/i
63
+ :isgd
64
+ when /b/i
65
+ :bitly
66
+ when /t/i
67
+ :tinyurl
68
+ else
69
+ :none
70
+ end
71
71
 
72
72
  sub(/_[ibt]$/i, "")
73
73
  end
@@ -89,10 +89,10 @@ module SL
89
89
  query = SL.query.map { |k, v| "#{k}=#{v}" }.join("&")
90
90
 
91
91
  query = if self =~ /\?[^= ]+=\S+/
92
- "&#{query}"
93
- else
94
- "?#{query}"
95
- end
92
+ "&#{query}"
93
+ else
94
+ "?#{query}"
95
+ end
96
96
 
97
97
  "#{self}#{query}"
98
98
  end
@@ -145,27 +145,29 @@ module SL
145
145
 
146
146
  # parse command line flags into long options
147
147
  def parse_flags
148
- gsub(/(\+\+|--)([dirtvs]+)\b/) do
148
+ gsub(/(\+\+|--)([dirtvsc]+)\b/) do
149
149
  m = Regexp.last_match
150
150
  bool = m[1] == "++" ? "" : "no-"
151
151
  output = " "
152
152
  m[2].split("").each do |arg|
153
153
  output += case arg
154
- when "d"
155
- "--#{bool}debug "
156
- when "i"
157
- "--#{bool}inline "
158
- when "r"
159
- "--#{bool}prefix_random "
160
- when "t"
161
- "--#{bool}include_titles "
162
- when "v"
163
- "--#{bool}validate_links "
164
- when "s"
165
- "--#{bool}remove_seo "
166
- else
167
- ""
168
- end
154
+ when "c"
155
+ "--#{bool}confirm"
156
+ when "d"
157
+ "--#{bool}debug "
158
+ when "i"
159
+ "--#{bool}inline "
160
+ when "r"
161
+ "--#{bool}prefix_random "
162
+ when "t"
163
+ "--#{bool}include_titles "
164
+ when "v"
165
+ "--#{bool}validate_links "
166
+ when "s"
167
+ "--#{bool}remove_seo "
168
+ else
169
+ ""
170
+ end
169
171
  end
170
172
 
171
173
  output
@@ -308,7 +310,7 @@ module SL
308
310
  "‘" => "’",
309
311
  "[" => "]",
310
312
  "(" => ")",
311
- "<" => ">"
313
+ "<" => ">",
312
314
  }
313
315
 
314
316
  left_punct = []
@@ -378,7 +380,7 @@ module SL
378
380
  # p_re = path.path_elements.map{|seg| seg.downcase.split(//).join('.?') }.join('|')
379
381
  # re_parts.push(p_re) if p_re.length > 0
380
382
 
381
- site_re = "(#{re_parts.join('|')})"
383
+ site_re = "(#{re_parts.join("|")})"
382
384
 
383
385
  dead_switch = 0
384
386
 
@@ -403,14 +405,14 @@ module SL
403
405
  end
404
406
 
405
407
  title = if parts.empty?
406
- longest
407
- elsif parts.length < 2
408
- parts.join(sep)
409
- elsif parts.length > 2
410
- parts.longest_element.strip
411
- else
412
- parts.join(sep)
413
- end
408
+ longest
409
+ elsif parts.length < 2
410
+ parts.join(sep)
411
+ elsif parts.length > 2
412
+ parts.longest_element.strip
413
+ else
414
+ parts.join(sep)
415
+ end
414
416
  end
415
417
  dead_switch += 1
416
418
  end
@@ -421,7 +423,7 @@ module SL
421
423
  return self
422
424
  end
423
425
 
424
- seps = Regexp.new(" *[#{seo_title_separators.map { |s| Regexp.escape(s) }.join('')}] +")
426
+ seps = Regexp.new(" *[#{seo_title_separators.map { |s| Regexp.escape(s) }.join("")}] +")
425
427
  if title =~ seps
426
428
  seo_parts = title.split(seps)
427
429
  title = seo_parts.longest_element.strip if seo_parts.length.positive?
@@ -524,13 +526,13 @@ module SL
524
526
  (1..n).each do |j|
525
527
  (1..m).each do |i|
526
528
  d[i][j] = if s[i - 1] == t[j - 1] # adjust index into string
527
- d[i - 1][j - 1] # no operation required
528
- else
529
- [d[i - 1][j] + 1, # deletion
530
- d[i][j - 1] + 1, # insertion
531
- d[i - 1][j - 1] + 1 # substitution
532
- ].min
533
- end
529
+ d[i - 1][j - 1] # no operation required
530
+ else
531
+ [d[i - 1][j] + 1, # deletion
532
+ d[i][j - 1] + 1, # insertion
533
+ d[i - 1][j - 1] + 1 # substitution
534
+ ].min
535
+ end
534
536
  end
535
537
  end
536
538
  d[m][n]
@@ -543,7 +545,7 @@ module SL
543
545
  ##
544
546
  def matches_exact(string)
545
547
  comp = gsub(/[^a-z0-9 ]/i, "")
546
- comp =~ /\b#{string.gsub(/[^a-z0-9 ]/i, '').split(/ +/).map { |s| Regexp.escape(s) }.join(' +')}/i
548
+ comp =~ /\b#{string.gsub(/[^a-z0-9 ]/i, "").split(/ +/).map { |s| Regexp.escape(s) }.join(" +")}/i
547
549
  end
548
550
 
549
551
  ##
@@ -591,7 +593,7 @@ module SL
591
593
  def to_rx_array(separator: " ", start_word: true)
592
594
  bound = start_word ? '\b' : ""
593
595
  str = gsub(/(#{separator})+/, separator)
594
- str.split(/#{separator}/).map { |arg| /#{bound}#{arg.gsub(/[^a-z0-9]/i, '.?')}/i }
596
+ str.split(/#{separator}/).map { |arg| /#{bound}#{arg.gsub(/[^a-z0-9]/i, ".?")}/i }
595
597
  end
596
598
 
597
599
  ##
@@ -146,6 +146,8 @@ module SL
146
146
  # end
147
147
 
148
148
  begin
149
+ return File.basename(url) if url =~ /\.(gif|jpe?g|png|web[mp]|bmp|svg|tiff?|pdf|avif)$/i
150
+
149
151
  if url =~ %r{https://(amzn.to|(www\.)?amazon\.com)/}
150
152
  final_url = follow_redirects(url)
151
153
  m = final_url.match(%r{https://www.amazon.com/(.*?)/dp/})
@@ -56,9 +56,10 @@ module SL
56
56
  ## @return [Array] url, title, link_text
57
57
  ##
58
58
  def search_with_timeout(search, timeout)
59
- url = nil
60
- title = nil
61
- link_text = nil
59
+ if SL.config["skip_timeout"] || SL.config["confirm"]
60
+ url, title, link_text = search.call
61
+ return [url, title, link_text]
62
+ end
62
63
 
63
64
  begin
64
65
  Timeout.timeout(timeout) do
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SL
4
- VERSION = "2.3.85"
4
+ VERSION = '2.3.86'
5
5
  end
6
6
 
7
7
  # Main module
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchlink
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.85
4
+ version: 2.3.86
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-30 00:00:00.000000000 Z
10
+ date: 2025-03-31 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64
@@ -287,7 +287,6 @@ files:
287
287
  - lib/searchlink/searches.rb
288
288
  - lib/searchlink/searches/amazon.rb
289
289
  - lib/searchlink/searches/applemusic.rb
290
- - lib/searchlink/searches/bitly.rb
291
290
  - lib/searchlink/searches/definition.rb
292
291
  - lib/searchlink/searches/duckduckgo.rb
293
292
  - lib/searchlink/searches/github.rb
@@ -297,19 +296,22 @@ files:
297
296
  - lib/searchlink/searches/helpers/safari.rb
298
297
  - lib/searchlink/searches/history.rb
299
298
  - lib/searchlink/searches/hook.rb
300
- - lib/searchlink/searches/isgd.rb
301
299
  - lib/searchlink/searches/itunes.rb
302
300
  - lib/searchlink/searches/lastfm.rb
303
301
  - lib/searchlink/searches/linkding.rb
304
302
  - lib/searchlink/searches/lyrics.rb
305
303
  - lib/searchlink/searches/pinboard.rb
304
+ - lib/searchlink/searches/popup.rb
306
305
  - lib/searchlink/searches/setapp.rb
306
+ - lib/searchlink/searches/shortener.rb
307
+ - lib/searchlink/searches/shorteners/bitly.rb
308
+ - lib/searchlink/searches/shorteners/isgd.rb
309
+ - lib/searchlink/searches/shorteners/tinyurl.rb
307
310
  - lib/searchlink/searches/social.rb
308
311
  - lib/searchlink/searches/software.rb
309
312
  - lib/searchlink/searches/spelling.rb
310
313
  - lib/searchlink/searches/spotlight.rb
311
314
  - lib/searchlink/searches/stackoverflow.rb
312
- - lib/searchlink/searches/tinyurl.rb
313
315
  - lib/searchlink/searches/tmdb.rb
314
316
  - lib/searchlink/searches/twitter.rb
315
317
  - lib/searchlink/searches/wikipedia.rb