-
Notifications
You must be signed in to change notification settings - Fork 0
/
ddg_search.rb
144 lines (125 loc) · 4.25 KB
/
ddg_search.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
require "cgi"
module Jekyll
class DdgSearchTag < Liquid::Tag
safe = true
priority = :low
def initialize(tag_name, text, tokens)
super
@tag = tag_name
@text = text
@search_url = 'http://duckduckgo.com/search.html'
# options that control output
@tag_options = ['frameborder', 'buttontext']
# see https://duckduckgo.com/search_box
@simple_options = ['width', 'duck', 'site', 'prefill', 'bgcolor', 'focus']
# see https://duckduckgo.com/params
@advanced_options = [
'kp', 'kl', 'ki', 'kz', 'kc', 'kn', 'kf', 'kb', 'kd', 'kg', 'kh', 'kj',
'ky', 'kx', 'k7', 'k8', 'k9', 'kaa', 'kab', 'ks', 'kw', 'km', 'ka',
'ku', 'kt', 'k2', 'ko', 'k3', 'kk', 'ke', 'kr', 'kq', 'k1', 'kv', 'k4',
't', 'sites'
]
@valid_options = @simple_options + @advanced_options + @tag_options
@options = get_options(text)
@frameborder =
escape_options('frameborder', @options.delete('frameborder'))
end
# Parse options from text string key:var pairs. Options with invalid keys
# or nil values are removed
def get_options(opt_string)
parsed_opts = fixup_options(
Hash[opt_string.split(/\s+/).map{|opt| opt.split(':')}].
delete_if{|k, v| (!@valid_options.include?(k)) || (v == nil) }
)
end
# Handle options that need some special treatment
def fixup_options(opts)
# The iframe version uses the 'site' parameter, while the form version
# uses 'sites'. Conver one to the other where necessary
if @tag == 'ddg_search'
if opts['site'] == nil && opts['sites'] != nil
opts['site'] = opts.delete('sites')
end
else
if opts['sites'] == nil && opts['site'] != nil
opts['sites'] = opts.delete('site')
end
end
return opts
end
# Escape query parameters, with special handling for prefill option
#
# Since prefill is a prompt phrase, it will probably require spaces. In the
# tag, spaces need to be entered as '+' so the tag options can be split
# correctly. Pluses are then escaped as '%2B' which are finally converted
# back to spaces.
#
# It would be better if DuckDuckGo handled escaped spaces better in their
# form generation.
def escape_options(key, value)
if value == nil
return value
end
value = CGI::escape(value)
if ['prefill', 'buttontext'].include?('prefill')
value.gsub!('%2B', ' ')
end
return value
end
# Convert options to URL params
def options_query(opts)
opts.map{|k,v| "#{k}=#{escape_options(k, v)}"}.join('&')
end
# Output iframe version of search box
def render_iframe(context)
url = [@search_url, options_query(@options)].join('?')
border = ''
if @frameborder != nil
border = %Q| frameborder="#{@frameborder}"|
end
return %Q|<iframe src="#{url}"#{border}></iframe>|
end
# Generate hidden form inputs
def hidden_inputs(opts)
opts.reject{|k, v| !@advanced_options.include?(k)}.map{|k, v|
%Q|<input type="hidden" name="#{k}" value="#{v}"/>|
}.join(' ')
end
# Generate search box form form version
def search_input(opts)
placeholder = ''
if opts.has_key?('prefill')
placeholder =
%Q| placeholder="#{escape_options('prefill',opts['prefill'])}"|
end
return %Q|<input type="text" name="q" maxlength="255"#{placeholder}/>|
end
# Generate text for submit button
def search_button(opts)
if opts['buttontext'] != nil
return escape_options('buttontext', opts['buttontext'])
end
return 'Search'
end
# Generate form version of search box
def render_form(context)
inputs =
<<HTML
<form method="get" id="search" action="http://duckduckgo.com/">
#{hidden_inputs(@options)}
#{search_input(@options)}
<input type="submit" value="#{search_button(@options)}" />
</form>
HTML
end
def render(context)
if @tag == 'ddg_search'
render_iframe(context)
else
render_form(context)
end
end
end
end
Liquid::Template.register_tag('ddg_search', Jekyll::DdgSearchTag)
Liquid::Template.register_tag('ddg_search_form', Jekyll::DdgSearchTag)