searchlink 2.3.85 → 2.3.87
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 +4 -4
- data/bin/searchlink +20 -19
- data/lib/searchlink/config.rb +8 -0
- data/lib/searchlink/curl/html.rb +20 -9
- data/lib/searchlink/curl/json.rb +5 -7
- data/lib/searchlink/help.rb +6 -2
- data/lib/searchlink/output.rb +1 -10
- data/lib/searchlink/parse.rb +78 -62
- data/lib/searchlink/plist/generator.rb +178 -0
- data/lib/searchlink/plist/parser.rb +263 -0
- data/lib/searchlink/plist/version.rb +5 -0
- data/lib/searchlink/plist.rb +12 -202
- data/lib/searchlink/script_plugin.rb +1 -3
- data/lib/searchlink/search.rb +1 -5
- data/lib/searchlink/searches/google.rb +3 -1
- data/lib/searchlink/searches/history.rb +5 -5
- data/lib/searchlink/searches/linkding.rb +2 -2
- data/lib/searchlink/searches/popup.rb +67 -0
- data/lib/searchlink/searches/setapp.rb +3 -1
- data/lib/searchlink/searches/shortener.rb +81 -0
- data/lib/searchlink/searches/{bitly.rb → shorteners/bitly.rb} +2 -1
- data/lib/searchlink/searches/{tinyurl.rb → shorteners/tinyurl.rb} +2 -1
- data/lib/searchlink/searches/social.rb +33 -28
- data/lib/searchlink/searches/twitter.rb +11 -12
- data/lib/searchlink/searches.rb +10 -11
- data/lib/searchlink/string.rb +3 -1
- data/lib/searchlink/url.rb +7 -2
- data/lib/searchlink/util.rb +17 -3
- data/lib/searchlink/version.rb +4 -4
- data/lib/searchlink.rb +3 -3
- metadata +10 -5
- /data/lib/searchlink/searches/{isgd.rb → shorteners/isgd.rb} +0 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# = plist
|
4
|
+
#
|
5
|
+
# Copyright 2006-2010 Ben Bleything and Patrick May
|
6
|
+
# Distributed under the MIT License
|
7
|
+
#
|
8
|
+
|
9
|
+
module Plist
|
10
|
+
# === Create a plist
|
11
|
+
# You can dump an object to a plist in one of two ways:
|
12
|
+
#
|
13
|
+
# * <tt>Plist::Emit.dump(obj)</tt>
|
14
|
+
# * <tt>obj.to_plist</tt>
|
15
|
+
# * This requires that you mixin the <tt>Plist::Emit</tt> module, which is already done for +Array+ and +Hash+.
|
16
|
+
#
|
17
|
+
# The following Ruby classes are converted into native plist types:
|
18
|
+
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false
|
19
|
+
# * +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the <array> and <dict> containers (respectively).
|
20
|
+
# * +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a <data> element.
|
21
|
+
# * User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to <tt>Marshal.dump</tt> and the result placed in a <data> element.
|
22
|
+
#
|
23
|
+
# For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below.
|
24
|
+
module Emit
|
25
|
+
DEFAULT_INDENT = "\t"
|
26
|
+
|
27
|
+
# Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+.
|
28
|
+
def to_plist(envelope = true, options = {})
|
29
|
+
Plist::Emit.dump(self, envelope, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+.
|
33
|
+
def save_plist(filename, options = {})
|
34
|
+
Plist::Emit.save_plist(self, filename, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# The following Ruby classes are converted into native plist types:
|
38
|
+
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time
|
39
|
+
#
|
40
|
+
# Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes.
|
41
|
+
#
|
42
|
+
# +IO+ and +StringIO+ objects are encoded and placed in <data> elements; other objects are <tt>Marshal.dump</tt>'ed unless they implement +to_plist_node+.
|
43
|
+
#
|
44
|
+
# The +envelope+ parameters dictates whether or not the resultant plist fragment is wrapped in the normal XML/plist header and footer. Set it to false if you only want the fragment.
|
45
|
+
def self.dump(obj, envelope = true, options = {})
|
46
|
+
options = { :indent => DEFAULT_INDENT }.merge(options)
|
47
|
+
|
48
|
+
output = PlistBuilder.new(options[:indent]).build(obj)
|
49
|
+
output = wrap(output) if envelope
|
50
|
+
|
51
|
+
output
|
52
|
+
end
|
53
|
+
|
54
|
+
# Writes the serialized object's plist to the specified filename.
|
55
|
+
def self.save_plist(obj, filename, options = {})
|
56
|
+
File.open(filename, 'wb') do |f|
|
57
|
+
f.write(obj.to_plist(true, options))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
class PlistBuilder
|
64
|
+
def initialize(indent_str)
|
65
|
+
@indent_str = indent_str.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def build(element, level=0)
|
69
|
+
if element.respond_to? :to_plist_node
|
70
|
+
element.to_plist_node
|
71
|
+
else
|
72
|
+
case element
|
73
|
+
when Array
|
74
|
+
if element.empty?
|
75
|
+
tag('array', nil, level)
|
76
|
+
else
|
77
|
+
tag('array', nil, level) {
|
78
|
+
element.collect {|e| build(e, level + 1) }.join
|
79
|
+
}
|
80
|
+
end
|
81
|
+
when Hash
|
82
|
+
if element.empty?
|
83
|
+
tag('dict', nil, level)
|
84
|
+
else
|
85
|
+
tag('dict', '', level) do
|
86
|
+
element.sort_by{|k,v| k.to_s }.collect do |k,v|
|
87
|
+
tag('key', CGI.escapeHTML(k.to_s), level + 1) +
|
88
|
+
build(v, level + 1)
|
89
|
+
end.join
|
90
|
+
end
|
91
|
+
end
|
92
|
+
when true, false
|
93
|
+
tag(element, nil, level)
|
94
|
+
when Time
|
95
|
+
tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), level)
|
96
|
+
when Date # also catches DateTime
|
97
|
+
tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'), level)
|
98
|
+
when String, Symbol, Integer, Float
|
99
|
+
tag(element_type(element), CGI.escapeHTML(element.to_s), level)
|
100
|
+
when IO, StringIO
|
101
|
+
data = element.tap(&:rewind).read
|
102
|
+
data_tag(data, level)
|
103
|
+
else
|
104
|
+
data = Marshal.dump(element)
|
105
|
+
comment_tag('The <data> element below contains a Ruby object which has been serialized with Marshal.dump.') +
|
106
|
+
data_tag(data, level)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def tag(type, contents, level, &block)
|
114
|
+
if block_given?
|
115
|
+
indent("<#{type}>\n", level) +
|
116
|
+
block.call +
|
117
|
+
indent("</#{type}>\n", level)
|
118
|
+
elsif contents.to_s.empty?
|
119
|
+
indent("<#{type}/>\n", level)
|
120
|
+
else
|
121
|
+
indent("<#{type}>#{contents.to_s}</#{type}>\n", level)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def data_tag(data, level)
|
126
|
+
# note that apple plists are wrapped at a different length then
|
127
|
+
# what ruby's pack wraps by default.
|
128
|
+
tag('data', nil, level) do
|
129
|
+
[data].pack("m") # equivalent to Base64.encode64(data)
|
130
|
+
.gsub(/\s+/, '')
|
131
|
+
.scan(/.{1,68}/o)
|
132
|
+
.collect { |line| indent(line, level) }
|
133
|
+
.join("\n")
|
134
|
+
.concat("\n")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def indent(str, level)
|
139
|
+
@indent_str.to_s * level + str
|
140
|
+
end
|
141
|
+
|
142
|
+
def element_type(item)
|
143
|
+
case item
|
144
|
+
when String, Symbol
|
145
|
+
'string'
|
146
|
+
when Integer
|
147
|
+
'integer'
|
148
|
+
when Float
|
149
|
+
'real'
|
150
|
+
else
|
151
|
+
raise "Don't know about this data type... something must be wrong!"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def comment_tag(content)
|
156
|
+
return "<!-- #{content} -->\n"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.wrap(contents)
|
161
|
+
output = '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
|
162
|
+
output << '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
|
163
|
+
output << '<plist version="1.0">' + "\n"
|
164
|
+
output << contents
|
165
|
+
output << '</plist>' + "\n"
|
166
|
+
|
167
|
+
output
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class Array #:nodoc:
|
173
|
+
include Plist::Emit
|
174
|
+
end
|
175
|
+
|
176
|
+
class Hash #:nodoc:
|
177
|
+
include Plist::Emit
|
178
|
+
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# = plist
|
4
|
+
#
|
5
|
+
# Copyright 2006-2010 Ben Bleything and Patrick May
|
6
|
+
# Distributed under the MIT License
|
7
|
+
#
|
8
|
+
|
9
|
+
# Plist parses Mac OS X xml property list files into ruby data structures.
|
10
|
+
#
|
11
|
+
# === Load a plist file
|
12
|
+
# This is the main point of the library:
|
13
|
+
#
|
14
|
+
# r = Plist.parse_xml(filename_or_xml)
|
15
|
+
module Plist
|
16
|
+
# Raised when an element is not implemented
|
17
|
+
class UnimplementedElementError < RuntimeError; end
|
18
|
+
|
19
|
+
# Note that I don't use these two elements much:
|
20
|
+
#
|
21
|
+
# + Date elements are returned as DateTime objects.
|
22
|
+
# + Data elements are implemented as Tempfiles
|
23
|
+
#
|
24
|
+
# Plist.parse_xml will blow up if it encounters a Date element.
|
25
|
+
# If you encounter such an error, or if you have a Date element which
|
26
|
+
# can't be parsed into a Time object, please create an issue
|
27
|
+
# attaching your plist file at https://github.com/patsplat/plist/issues
|
28
|
+
# so folks can implement the proper support.
|
29
|
+
#
|
30
|
+
# By default, <data> will be assumed to be a marshaled Ruby object and
|
31
|
+
# interpreted with <tt>Marshal.load</tt>. Pass <tt>marshal: false</tt>
|
32
|
+
# to disable this behavior and return the raw binary data as an IO
|
33
|
+
# object instead.
|
34
|
+
def self.parse_xml(filename_or_xml, options={})
|
35
|
+
listener = Listener.new(options)
|
36
|
+
# parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
|
37
|
+
parser = StreamParser.new(filename_or_xml, listener)
|
38
|
+
parser.parse
|
39
|
+
listener.result
|
40
|
+
end
|
41
|
+
|
42
|
+
class Listener
|
43
|
+
# include REXML::StreamListener
|
44
|
+
|
45
|
+
attr_accessor :result, :open
|
46
|
+
|
47
|
+
def initialize(options={})
|
48
|
+
@result = nil
|
49
|
+
@open = []
|
50
|
+
@options = { :marshal => true }.merge(options).freeze
|
51
|
+
end
|
52
|
+
|
53
|
+
def tag_start(name, attributes)
|
54
|
+
@open.push PTag.mappings[name].new(@options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def text(contents)
|
58
|
+
if @open.last
|
59
|
+
@open.last.text ||= ''.dup
|
60
|
+
@open.last.text.concat(contents)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def tag_end(name)
|
65
|
+
last = @open.pop
|
66
|
+
if @open.empty?
|
67
|
+
@result = last.to_ruby
|
68
|
+
else
|
69
|
+
@open.last.children.push last
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class StreamParser
|
75
|
+
def initialize(plist_data_or_file, listener)
|
76
|
+
if plist_data_or_file.respond_to? :read
|
77
|
+
@xml = plist_data_or_file.read
|
78
|
+
elsif File.exist? plist_data_or_file
|
79
|
+
@xml = File.read(plist_data_or_file)
|
80
|
+
else
|
81
|
+
@xml = plist_data_or_file
|
82
|
+
end
|
83
|
+
|
84
|
+
@listener = listener
|
85
|
+
end
|
86
|
+
|
87
|
+
TEXT = /([^<]+)/
|
88
|
+
CDATA = /<!\[CDATA\[(.*?)\]\]>/
|
89
|
+
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/m
|
90
|
+
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/m
|
91
|
+
COMMENT_START = /\A<!--/
|
92
|
+
COMMENT_END = /.*?-->/m
|
93
|
+
UNIMPLEMENTED_ERROR = 'Unimplemented element. ' \
|
94
|
+
'Consider reporting via https://github.com/patsplat/plist/issues'
|
95
|
+
|
96
|
+
def parse
|
97
|
+
plist_tags = PTag.mappings.keys.join('|')
|
98
|
+
start_tag = /<(#{plist_tags})([^>]*)>/i
|
99
|
+
end_tag = /<\/(#{plist_tags})[^>]*>/i
|
100
|
+
|
101
|
+
require 'strscan'
|
102
|
+
|
103
|
+
@scanner = StringScanner.new(@xml)
|
104
|
+
until @scanner.eos?
|
105
|
+
if @scanner.scan(COMMENT_START)
|
106
|
+
@scanner.scan(COMMENT_END)
|
107
|
+
elsif @scanner.scan(XMLDECL_PATTERN)
|
108
|
+
encoding = parse_encoding_from_xml_declaration(@scanner[1])
|
109
|
+
next if encoding.nil?
|
110
|
+
|
111
|
+
# use the specified encoding for the rest of the file
|
112
|
+
next unless String.method_defined?(:force_encoding)
|
113
|
+
@scanner.string = @scanner.rest.force_encoding(encoding)
|
114
|
+
elsif @scanner.scan(DOCTYPE_PATTERN)
|
115
|
+
next
|
116
|
+
elsif @scanner.scan(start_tag)
|
117
|
+
@listener.tag_start(@scanner[1], nil)
|
118
|
+
if (@scanner[2] =~ /\/$/)
|
119
|
+
@listener.tag_end(@scanner[1])
|
120
|
+
end
|
121
|
+
elsif @scanner.scan(TEXT)
|
122
|
+
@listener.text(@scanner[1])
|
123
|
+
elsif @scanner.scan(CDATA)
|
124
|
+
@listener.text(@scanner[1])
|
125
|
+
elsif @scanner.scan(end_tag)
|
126
|
+
@listener.tag_end(@scanner[1])
|
127
|
+
else
|
128
|
+
raise UnimplementedElementError.new(UNIMPLEMENTED_ERROR)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def parse_encoding_from_xml_declaration(xml_declaration)
|
136
|
+
return unless defined?(Encoding)
|
137
|
+
|
138
|
+
xml_encoding = xml_declaration.match(/(?:\A|\s)encoding=(?:"(.*?)"|'(.*?)')(?:\s|\Z)/)
|
139
|
+
|
140
|
+
return if xml_encoding.nil?
|
141
|
+
|
142
|
+
begin
|
143
|
+
Encoding.find(xml_encoding[1])
|
144
|
+
rescue ArgumentError
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class PTag
|
151
|
+
def self.mappings
|
152
|
+
@mappings ||= {}
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.inherited(sub_class)
|
156
|
+
key = sub_class.to_s.downcase
|
157
|
+
key.gsub!(/^plist::/, '')
|
158
|
+
key.gsub!(/^p/, '') unless key == "plist"
|
159
|
+
|
160
|
+
mappings[key] = sub_class
|
161
|
+
end
|
162
|
+
|
163
|
+
attr_accessor :text, :children, :options
|
164
|
+
def initialize(options)
|
165
|
+
@children = []
|
166
|
+
@options = options
|
167
|
+
end
|
168
|
+
|
169
|
+
def to_ruby
|
170
|
+
raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class PList < PTag
|
175
|
+
def to_ruby
|
176
|
+
children.first.to_ruby if children.first
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class PDict < PTag
|
181
|
+
def to_ruby
|
182
|
+
dict = {}
|
183
|
+
key = nil
|
184
|
+
|
185
|
+
children.each do |c|
|
186
|
+
if key.nil?
|
187
|
+
key = c.to_ruby
|
188
|
+
else
|
189
|
+
dict[key] = c.to_ruby
|
190
|
+
key = nil
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
dict
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class PKey < PTag
|
199
|
+
def to_ruby
|
200
|
+
CGI.unescapeHTML(text || '')
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class PString < PTag
|
205
|
+
def to_ruby
|
206
|
+
CGI.unescapeHTML(text || '')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
class PArray < PTag
|
211
|
+
def to_ruby
|
212
|
+
children.collect do |c|
|
213
|
+
c.to_ruby
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
class PInteger < PTag
|
219
|
+
def to_ruby
|
220
|
+
text.to_i
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class PTrue < PTag
|
225
|
+
def to_ruby
|
226
|
+
true
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
class PFalse < PTag
|
231
|
+
def to_ruby
|
232
|
+
false
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
class PReal < PTag
|
237
|
+
def to_ruby
|
238
|
+
text.to_f
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
require 'date'
|
243
|
+
class PDate < PTag
|
244
|
+
def to_ruby
|
245
|
+
DateTime.parse(text)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
class PData < PTag
|
250
|
+
def to_ruby
|
251
|
+
# unpack("m")[0] is equivalent to Base64.decode64
|
252
|
+
data = text.gsub(/\s+/, '').unpack("m")[0] unless text.nil?
|
253
|
+
begin
|
254
|
+
return Marshal.load(data) if options[:marshal]
|
255
|
+
rescue Exception
|
256
|
+
end
|
257
|
+
io = StringIO.new
|
258
|
+
io.write data
|
259
|
+
io.rewind
|
260
|
+
io
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
data/lib/searchlink/plist.rb
CHANGED
@@ -1,212 +1,22 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
3
|
# = plist
|
4
4
|
#
|
5
|
+
# This is the main file for plist. Everything interesting happens in
|
6
|
+
# Plist and Plist::Emit.
|
7
|
+
#
|
5
8
|
# Copyright 2006-2010 Ben Bleything and Patrick May
|
6
9
|
# Distributed under the MIT License
|
7
|
-
module Plist; end
|
8
|
-
|
9
|
-
# === Load a plist file
|
10
|
-
# This is the main point of the library:
|
11
10
|
#
|
12
|
-
# r = Plist::parse_xml( filename_or_xml )
|
13
|
-
module Plist
|
14
|
-
def self.parse_xml(filename_or_xml)
|
15
|
-
listener = Listener.new
|
16
|
-
parser = StreamParser.new(filename_or_xml, listener)
|
17
|
-
parser.parse
|
18
|
-
listener.result
|
19
|
-
end
|
20
|
-
|
21
|
-
class Listener
|
22
|
-
attr_accessor :result, :open
|
23
|
-
|
24
|
-
def initialize
|
25
|
-
@result = nil
|
26
|
-
@open = []
|
27
|
-
end
|
28
|
-
|
29
|
-
def tag_start(name, _attributes)
|
30
|
-
@open.push PTag.mappings[name].new
|
31
|
-
end
|
32
|
-
|
33
|
-
def text(contents)
|
34
|
-
@open.last.text = contents if @open.last
|
35
|
-
end
|
36
|
-
|
37
|
-
def tag_end(_name)
|
38
|
-
last = @open.pop
|
39
|
-
if @open.empty?
|
40
|
-
@result = last.to_ruby
|
41
|
-
else
|
42
|
-
@open.last.children.push last
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
class StreamParser
|
48
|
-
def initialize(plist_data_or_file, listener)
|
49
|
-
@xml = if plist_data_or_file.respond_to? :read
|
50
|
-
plist_data_or_file.read
|
51
|
-
elsif File.exist? plist_data_or_file
|
52
|
-
File.read(plist_data_or_file)
|
53
|
-
else
|
54
|
-
plist_data_or_file
|
55
|
-
end
|
56
|
-
|
57
|
-
@listener = listener
|
58
|
-
end
|
59
|
-
|
60
|
-
TEXT = /([^<]+)/.freeze
|
61
|
-
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um.freeze
|
62
|
-
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um.freeze
|
63
|
-
COMMENT_START = /\A<!--/u.freeze
|
64
|
-
COMMENT_END = /.*?-->/um.freeze
|
65
|
-
|
66
|
-
def parse
|
67
|
-
plist_tags = PTag.mappings.keys.join("|")
|
68
|
-
start_tag = /<(#{plist_tags})([^>]*)>/i
|
69
|
-
end_tag = %r{</(#{plist_tags})[^>]*>}i
|
70
|
-
|
71
|
-
require "strscan"
|
72
|
-
|
73
|
-
@scanner = StringScanner.new(@xml)
|
74
|
-
until @scanner.eos?
|
75
|
-
next unless @scanner.scan(COMMENT_START)
|
76
|
-
|
77
|
-
@scanner.scan(COMMENT_END)
|
78
|
-
if @scanner.scan(start_tag)
|
79
|
-
@listener.tag_start(@scanner[1], nil)
|
80
|
-
@listener.tag_end(@scanner[1]) if @scanner[2] =~ %r{/$}
|
81
|
-
elsif @scanner.scan(TEXT)
|
82
|
-
@listener.text(@scanner[1])
|
83
|
-
elsif @scanner.scan(end_tag)
|
84
|
-
@listener.tag_end(@scanner[1])
|
85
|
-
else
|
86
|
-
raise "Unimplemented element"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class PTag
|
92
|
-
@@mappings = {}
|
93
|
-
def self.mappings
|
94
|
-
@@mappings
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.inherited(sub_class)
|
98
|
-
key = sub_class.to_s.downcase
|
99
|
-
key.gsub!(/^plist::/, "")
|
100
|
-
key.gsub!(/^p/, "") unless key == "plist"
|
101
|
-
|
102
|
-
@@mappings[key] = sub_class
|
103
|
-
super
|
104
|
-
end
|
105
|
-
|
106
|
-
attr_accessor :text, :children
|
107
|
-
|
108
|
-
def initialize
|
109
|
-
@children = []
|
110
|
-
end
|
111
|
-
|
112
|
-
def to_ruby
|
113
|
-
raise "Unimplemented: #{self.class}#to_ruby on #{inspect}"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
class PList < PTag
|
118
|
-
def to_ruby
|
119
|
-
children.first&.to_ruby
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
class PDict < PTag
|
124
|
-
def to_ruby
|
125
|
-
dict = {}
|
126
|
-
key = nil
|
127
|
-
|
128
|
-
children.each do |c|
|
129
|
-
if key.nil?
|
130
|
-
key = c.to_ruby
|
131
|
-
else
|
132
|
-
dict[key] = c.to_ruby
|
133
|
-
key = nil
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
dict
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
class PKey < PTag
|
142
|
-
def to_ruby
|
143
|
-
CGI.unescapeHTML(text || "")
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
class PString < PTag
|
148
|
-
def to_ruby
|
149
|
-
CGI.unescapeHTML(text || "")
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
class PArray < PTag
|
154
|
-
def to_ruby
|
155
|
-
children.collect(&:to_ruby)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
class PInteger < PTag
|
160
|
-
def to_ruby
|
161
|
-
text.to_i
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
class PTrue < PTag
|
166
|
-
def to_ruby
|
167
|
-
true
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
class PFalse < PTag
|
172
|
-
def to_ruby
|
173
|
-
false
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
class PReal < PTag
|
178
|
-
def to_ruby
|
179
|
-
text.to_f
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
require "date"
|
184
|
-
|
185
|
-
class PDate < PTag
|
186
|
-
def to_ruby
|
187
|
-
DateTime.parse(text)
|
188
|
-
end
|
189
|
-
end
|
190
11
|
|
191
|
-
|
12
|
+
require "cgi"
|
13
|
+
require "stringio"
|
192
14
|
|
193
|
-
|
194
|
-
|
195
|
-
data = Base64.decode64(text.gsub(/\s+/, ""))
|
15
|
+
# import
|
16
|
+
require_relative "plist/generator"
|
196
17
|
|
197
|
-
|
198
|
-
|
199
|
-
rescue Exception
|
200
|
-
io = StringIO.new
|
201
|
-
io.write data
|
202
|
-
io.rewind
|
203
|
-
io
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
18
|
+
# import
|
19
|
+
require_relative "plist/parser"
|
209
20
|
|
210
|
-
#
|
211
|
-
|
212
|
-
# end
|
21
|
+
# import
|
22
|
+
require_relative "plist/version"
|
@@ -55,9 +55,7 @@ module SL
|
|
55
55
|
end
|
56
56
|
|
57
57
|
%w[url title link_text].each do |key|
|
58
|
-
unless res.key?(key)
|
59
|
-
raise PluginError.new(%("#{File.basename(@script)}" output missing key "#{key}"), plugin: @filename)
|
60
|
-
end
|
58
|
+
raise PluginError.new(%("#{File.basename(@script)}" output missing key "#{key}"), plugin: @filename) unless res.key?(key)
|
61
59
|
end
|
62
60
|
|
63
61
|
[res["url"], res["title"], res["link_text"]]
|
data/lib/searchlink/search.rb
CHANGED
@@ -5,8 +5,6 @@ module SL
|
|
5
5
|
include URL
|
6
6
|
|
7
7
|
class SearchLink
|
8
|
-
include Plist
|
9
|
-
|
10
8
|
attr_reader :originput, :output, :clipboard
|
11
9
|
|
12
10
|
private
|
@@ -37,9 +35,7 @@ module SL
|
|
37
35
|
else
|
38
36
|
case search_type
|
39
37
|
when /^r$/ # simple replacement
|
40
|
-
if SL.config["validate_links"] && !SL::URL.valid_link?(search_terms)
|
41
|
-
return [false, "Link not valid: #{search_terms}", link_text]
|
42
|
-
end
|
38
|
+
return [false, "Link not valid: #{search_terms}", link_text] if SL.config["validate_links"] && !SL::URL.valid_link?(search_terms)
|
43
39
|
|
44
40
|
title = SL::URL.title(search_terms) || search_terms
|
45
41
|
|
@@ -43,7 +43,9 @@ module SL
|
|
43
43
|
return false
|
44
44
|
end
|
45
45
|
|
46
|
-
url = "https://customsearch.googleapis.com/customsearch/v1?cx=338419ee5ac894523&q=#{ERB::Util.url_encode(search_terms.gsub(
|
46
|
+
url = "https://customsearch.googleapis.com/customsearch/v1?cx=338419ee5ac894523&q=#{ERB::Util.url_encode(search_terms.gsub(
|
47
|
+
'%22', '"'
|
48
|
+
))}&num=1&key=#{@api_key}"
|
47
49
|
json = Curl::Json.new(url).json
|
48
50
|
|
49
51
|
if json["error"] && json["error"]["code"].to_i == 429
|