picky-generators 4.2.4 → 4.3.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/prototypes/all_in_one/sinatra/app.rb +1 -7
- data/prototypes/all_in_one/sinatra/book.rb +10 -10
- data/prototypes/all_in_one/sinatra/images/picky.png +0 -0
- data/prototypes/all_in_one/sinatra/javascripts/picky.min.js +19 -17
- data/prototypes/all_in_one/sinatra/stylesheets/application.css +82 -18
- data/prototypes/all_in_one/sinatra/stylesheets/images/background.png +0 -0
- data/prototypes/all_in_one/sinatra/stylesheets/images/cancel.svg +10 -0
- data/prototypes/all_in_one/sinatra/stylesheets/picky.css +277 -135
- data/prototypes/all_in_one/sinatra/views/search.haml +232 -58
- data/prototypes/client/sinatra/app.rb +19 -30
- data/prototypes/client/sinatra/book.rb +9 -9
- data/prototypes/client/sinatra/images/picky.png +0 -0
- data/prototypes/client/sinatra/javascripts/picky.min.js +19 -17
- data/prototypes/client/sinatra/stylesheets/application.css +82 -18
- data/prototypes/client/sinatra/stylesheets/images/background.png +0 -0
- data/prototypes/client/sinatra/stylesheets/images/cancel.svg +10 -0
- data/prototypes/client/sinatra/stylesheets/picky.css +277 -135
- data/prototypes/client/sinatra/views/search.haml +232 -58
- metadata +14 -12
- data/prototypes/all_in_one/sinatra/views/configure.haml +0 -188
- data/prototypes/client/sinatra/views/configure.haml +0 -188
@@ -1,60 +1,232 @@
|
|
1
1
|
!!!
|
2
2
|
%html{ :lang => 'en' }
|
3
3
|
%head
|
4
|
-
%link{:href => "stylesheets/picky.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}/
|
5
4
|
%link{:href => "stylesheets/application.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}/
|
6
|
-
|
5
|
+
%link{:href => "stylesheets/picky.css", :media => "screen", :rel => "stylesheet", :type => "text/css"}/
|
7
6
|
= js 'jquery-1.5.0.min'
|
8
7
|
= js 'history.min'
|
9
8
|
= js 'history.adapter.jquery.min'
|
10
9
|
= js 'picky.min'
|
11
10
|
|
12
11
|
%body
|
13
|
-
%
|
14
|
-
|
15
|
-
%
|
16
|
-
|
17
|
-
%
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
A simple word,
|
24
|
-
= succeed "." do
|
25
|
-
%a{:href => "#", :onclick => "pickyClient.insert('alan');"} alan
|
26
|
-
%span.explanation
|
27
|
-
(Finds Alan in the title, and Alans who wrote books. The title is ranked higher due to weighing.)
|
28
|
-
%span
|
29
|
-
%p
|
30
|
-
With qualifier,
|
31
|
-
= succeed "." do
|
32
|
-
%a{:href => "#", :onclick => "pickyClient.insert('title:women');"} title:women
|
33
|
-
%span.explanation
|
34
|
-
(Finds "women*" just in titles.)
|
35
|
-
%span
|
36
|
-
%p
|
37
|
-
With similarity,
|
38
|
-
= succeed "." do
|
39
|
-
%a{:href => "#", :onclick => "pickyClient.insert('pinchn~');"} pinchn~
|
40
|
-
%span.explanation (Finds "pynchon", note: Only title in example with similarity. Uses a combination of double metaphone and Levenshtein.)
|
41
|
-
%p
|
42
|
-
More complex,
|
43
|
-
= succeed "." do
|
44
|
-
%a{:href => "#", :onclick => "pickyClient.insert('title:lyterature~');"} title:lyterature~
|
45
|
-
%span.explanation (Finds similar titles)
|
46
|
-
%p
|
47
|
-
With choice,
|
48
|
-
= succeed "." do
|
49
|
-
%a{:href => "#", :onclick => "pickyClient.insert('sp');"} sp
|
50
|
-
%span.explanation (Finds "sp*" in multiple categories. Choose the one you're looking for.)
|
51
|
-
%p
|
52
|
-
More complex,
|
53
|
-
= succeed "." do
|
54
|
-
%a{:href => "#", :onclick => "pickyClient.insert('soc* p');"} soc* p
|
55
|
-
%span.explanation (This is where Picky really shines, "the title started with soc, and the author starts with p")
|
12
|
+
%hgroup
|
13
|
+
/ Replace these headers with your code
|
14
|
+
%img{ :class => 'picky', :src => "images/picky.png"}/
|
15
|
+
%h1 Picky
|
16
|
+
%h2
|
17
|
+
Scaffold: A great start into your
|
18
|
+
%em fantastic
|
19
|
+
search engine!
|
20
|
+
☛
|
21
|
+
%a{ :href => "http://florianhanke.com/picky" } Picky Homepage
|
56
22
|
.content
|
57
23
|
= Picky::Helper.cached_interface
|
24
|
+
%section
|
25
|
+
%h1 Examples:
|
26
|
+
%p
|
27
|
+
%a{:href => "", :onclick => "pickyClient.insert('alan');"} alan
|
28
|
+
finds Alan in the title, and Alans who wrote books. The title is ranked higher due to weighing.
|
29
|
+
%p
|
30
|
+
%a{:href => "", :onclick => "pickyClient.insert('title:women');"} title:women
|
31
|
+
finds "women*" just in titles.
|
32
|
+
%p
|
33
|
+
%a{:href => "", :onclick => "pickyClient.insert('pinchn~');"} pinchn~
|
34
|
+
finds the similar "pynchon".
|
35
|
+
%p
|
36
|
+
%a{:href => "", :onclick => "pickyClient.insert('title:lyterature~');"} title:lyterature~
|
37
|
+
finds similar titles.
|
38
|
+
%p
|
39
|
+
%a{:href => "", :onclick => "pickyClient.insert('sp');"} sp
|
40
|
+
finds "sp*" in multiple categories. Choose the one you're looking for.
|
41
|
+
%p
|
42
|
+
%a{:href => "", :onclick => "pickyClient.insert('soc* p');"} soc* p
|
43
|
+
means "find something that starts with soc, and another thing starting with p".
|
44
|
+
%section
|
45
|
+
%h1 Already made it this far? You're good!
|
46
|
+
%p I think you're ready for configuring me for your own purposes.
|
47
|
+
%h2 Configuring the Picky client.
|
48
|
+
%p
|
49
|
+
There are two places where you configure the Picky client:
|
50
|
+
%ol
|
51
|
+
%li
|
52
|
+
%a{ :href => '#controller' } In the controller.
|
53
|
+
(client to Picky server)
|
54
|
+
%li
|
55
|
+
%a{ :href => '#view' } In the view.
|
56
|
+
(javascript client interface)
|
57
|
+
%h2#controller Controller
|
58
|
+
%p
|
59
|
+
Open the file
|
60
|
+
%strong app.rb
|
61
|
+
and take a peek inside.
|
62
|
+
%p
|
63
|
+
First you need both a client instance for a
|
64
|
+
%strong full
|
65
|
+
or a
|
66
|
+
%strong live search
|
67
|
+
depending on what you need (Full gets results with IDs, Live without and is used for updating the counter).
|
68
|
+
%p
|
69
|
+
In the example, I called it
|
70
|
+
%strong BookSearch
|
71
|
+
\.
|
72
|
+
%code
|
73
|
+
%pre
|
74
|
+
:preserve
|
75
|
+
BookSearch = Picky::Client.new host: 'localhost',
|
76
|
+
port: 8080,
|
77
|
+
path: '/books'
|
78
|
+
%p
|
79
|
+
Both clients offer the options:
|
80
|
+
%dl
|
81
|
+
%dt
|
82
|
+
%strong host
|
83
|
+
%dd The Picky search server host.
|
84
|
+
%dt
|
85
|
+
%strong port
|
86
|
+
%dd The Picky search server port (see unicorn.rb in the server).
|
87
|
+
%dt
|
88
|
+
%strong path
|
89
|
+
%dd The Picky search path (see app.rb in the server).
|
90
|
+
%p
|
91
|
+
Then, use these Client instances in your actions. For example like this:
|
92
|
+
%code
|
93
|
+
%pre
|
94
|
+
:preserve
|
95
|
+
get '/search/full' do
|
96
|
+
results = BookSearch.search params[:query],
|
97
|
+
:ids => params[:ids],
|
98
|
+
:offset => params[:offset]
|
99
|
+
results.extend Picky::Convenience
|
100
|
+
results.populate_with Book do |book|
|
101
|
+
book.to_s
|
102
|
+
end
|
103
|
+
ActiveSupport::JSON.encode results
|
104
|
+
end
|
105
|
+
%p
|
106
|
+
This part gets a
|
107
|
+
%strong hash
|
108
|
+
with the results:
|
109
|
+
%code
|
110
|
+
%pre
|
111
|
+
:preserve
|
112
|
+
results = BookSearch.search params[:query],
|
113
|
+
:ids => params[:ids],
|
114
|
+
:offset => params[:offset]
|
115
|
+
%p
|
116
|
+
This part takes the
|
117
|
+
%strong hash
|
118
|
+
and extends it with a few useful and convenient methods:
|
119
|
+
%code
|
120
|
+
%pre results.extend Picky::Convenience
|
121
|
+
%p
|
122
|
+
One of these methods is the
|
123
|
+
%strong populate_with
|
124
|
+
method which takes a
|
125
|
+
%strong model
|
126
|
+
as parameter, and then gets the corresponding
|
127
|
+
%strong model instances
|
128
|
+
for each id in the result.
|
129
|
+
%code
|
130
|
+
%pre
|
131
|
+
:preserve
|
132
|
+
results.populate_with Book do |book|
|
133
|
+
book.to_s
|
134
|
+
end
|
135
|
+
The
|
136
|
+
%strong book.to_s
|
137
|
+
simulates rendering a book.
|
138
|
+
You can do whatever with the book instance if you just return a rendered thing that's supposed to be shown in the front end.
|
139
|
+
%p
|
140
|
+
If you don't want to render in the controller, you can do so in a view. In the controller just call
|
141
|
+
%code
|
142
|
+
%pre
|
143
|
+
results.populate_with Book
|
144
|
+
and then render the books in a view using:
|
145
|
+
%code
|
146
|
+
%pre
|
147
|
+
:preserve
|
148
|
+
results.entries do |book|
|
149
|
+
render book # or book.to_s, or however you like to render the book.
|
150
|
+
end
|
151
|
+
%p
|
152
|
+
At the end, encode the hash in JSON:
|
153
|
+
%code
|
154
|
+
%pre
|
155
|
+
ActiveSupport::JSON.encode results
|
156
|
+
That's it for the controller.
|
157
|
+
%p
|
158
|
+
All the steps in one:
|
159
|
+
%code
|
160
|
+
%pre
|
161
|
+
:preserve
|
162
|
+
BookSearch = Picky::Client.new host: 'localhost',
|
163
|
+
port: 8080,
|
164
|
+
path: '/books'
|
165
|
+
|
166
|
+
get '/search/full' do
|
167
|
+
results = BookSearch.search params[:query],
|
168
|
+
ids: params[:ids],
|
169
|
+
offset: params[:offset]
|
170
|
+
|
171
|
+
results.extend Picky::Convenience
|
172
|
+
results.populate_with Book do |book|
|
173
|
+
book.to_s
|
174
|
+
end
|
175
|
+
|
176
|
+
ActiveSupport::JSON.encode results
|
177
|
+
end
|
178
|
+
%h2#view View
|
179
|
+
%p
|
180
|
+
The view is even easier. Just add the line
|
181
|
+
%code
|
182
|
+
%pre
|
183
|
+
:preserve
|
184
|
+
= Picky::Helper.cached_interface
|
185
|
+
if you use just one language, or
|
186
|
+
%code
|
187
|
+
%pre
|
188
|
+
:preserve
|
189
|
+
= Picky::Helper.interface button: 'search',
|
190
|
+
no_results: 'No results!',
|
191
|
+
more: 'more'
|
192
|
+
if you use multiple languages.
|
193
|
+
(You'd use the options
|
194
|
+
%code
|
195
|
+
%pre
|
196
|
+
:preserve
|
197
|
+
button: t(:'search.button'),
|
198
|
+
no_results: t(:'search.no_results'),
|
199
|
+
more: t(:'search.more')
|
200
|
+
of course, with proper i18n)
|
201
|
+
The same options can be used for
|
202
|
+
%strong #cached_interface
|
203
|
+
but they will be cached, so you can only set them once and then reused.
|
204
|
+
%p
|
205
|
+
You're almost finished. Take a look at the file
|
206
|
+
%strong views/search.haml
|
207
|
+
where you'll find javascript at the end. It looks something like this:
|
208
|
+
%code
|
209
|
+
%pre
|
210
|
+
:preserve
|
211
|
+
:javascript
|
212
|
+
pickyClient = new PickyClient({
|
213
|
+
// A full query displays the rendered results.
|
214
|
+
//
|
215
|
+
full: '/search/full',
|
216
|
+
|
217
|
+
// etc.
|
218
|
+
Just take a look at the possible javascript client options in that file.
|
219
|
+
%p
|
220
|
+
%strong Good luck my friend!
|
221
|
+
%em *waves several stubby pink tentacles*
|
222
|
+
%footer
|
223
|
+
Website design by
|
224
|
+
%a{ :href => 'http://twitter.com/thijs' } Thijs van der Vossen
|
225
|
+
of
|
226
|
+
%a{ :href => 'http://fngtps.com' } Fingertips
|
227
|
+
with modifications by
|
228
|
+
= succeed('.') do
|
229
|
+
%a{ :href => 'http://twitter.com/hanke' } Florian Hanke
|
58
230
|
:javascript
|
59
231
|
$(window).load(function() {
|
60
232
|
pickyClient = new PickyClient({
|
@@ -63,13 +235,12 @@
|
|
63
235
|
full: '/search/full',
|
64
236
|
// fullResults: 100, // Optional. Amount of ids to search for, default 20.
|
65
237
|
|
66
|
-
// A live query just updates the count
|
67
|
-
// to render (could go straight to the search server).
|
238
|
+
// A live query just updates the count.
|
68
239
|
//
|
69
240
|
live: '/search/live',
|
70
241
|
// liveResults: 0, // Optional. Amount of ids to search for, default 0.
|
71
242
|
|
72
|
-
// showResultsLimit:
|
243
|
+
// showResultsLimit: 100, // Optional. Default is 10.
|
73
244
|
|
74
245
|
// Wrap each li group (like author-title, or title-isbn etc.) of results
|
75
246
|
// in this element.
|
@@ -77,7 +248,7 @@
|
|
77
248
|
//
|
78
249
|
// wrapResults: '<div class="hello"><ol class="world"></ol></div>',
|
79
250
|
|
80
|
-
// before: function(
|
251
|
+
// before: function(query, params) { }, // Optional. Before Picky sends any data. Return modified query.
|
81
252
|
// success: function(data, query) { }, // Optional. Just after Picky receives data. (Get a PickyData object)
|
82
253
|
// after: function(data, query) { }, // Optional. After Picky has handled the data and updated the view.
|
83
254
|
|
@@ -103,14 +274,17 @@
|
|
103
274
|
choices: {
|
104
275
|
en:{
|
105
276
|
'title': {
|
106
|
-
format: "<strong>%1$s</strong>",
|
277
|
+
format: "Called <strong>%1$s</strong>",
|
107
278
|
filter: function(text) { return text.toUpperCase(); },
|
108
|
-
ignoreSingle:
|
279
|
+
ignoreSingle: true
|
109
280
|
},
|
110
|
-
'author
|
111
|
-
'
|
112
|
-
'
|
113
|
-
'author,
|
281
|
+
'author': 'Written by %1$s',
|
282
|
+
'subjects': 'Being about %1$s',
|
283
|
+
'publisher': 'Published by %1$s',
|
284
|
+
'author,title': 'Called %1$s, written by %2$s',
|
285
|
+
'title,author': 'Called %2$s, written by %1$s',
|
286
|
+
'title,subjects': 'Called %1$s, about %2$s',
|
287
|
+
'author,subjects': '%1$s who wrote about %2$s'
|
114
288
|
}
|
115
289
|
},
|
116
290
|
|
@@ -122,9 +296,9 @@
|
|
122
296
|
en:{
|
123
297
|
title: 'titled',
|
124
298
|
author: 'written by',
|
125
|
-
year: 'published in'
|
126
|
-
|
127
|
-
|
299
|
+
year: 'published in',
|
300
|
+
publisher: 'published by',
|
301
|
+
subjects: 'with subjects'
|
128
302
|
}
|
129
303
|
}
|
130
304
|
});
|
@@ -2,22 +2,21 @@ require 'rubygems'
|
|
2
2
|
require 'bundler'
|
3
3
|
Bundler.require
|
4
4
|
|
5
|
-
#
|
5
|
+
# Load the "model".
|
6
6
|
#
|
7
|
-
|
8
|
-
set :public, File.dirname(__FILE__)
|
9
|
-
set :views, File.expand_path('../views', __FILE__)
|
10
|
-
set :haml, :format => :html5
|
7
|
+
require File.expand_path 'book', File.dirname(__FILE__)
|
11
8
|
|
12
|
-
|
13
|
-
#
|
14
|
-
require File.expand_path '../book', __FILE__
|
9
|
+
set :haml, { :format => :html5 }
|
15
10
|
|
16
|
-
# Sets up
|
11
|
+
# Sets up two query instances.
|
17
12
|
#
|
18
|
-
|
13
|
+
BooksSearch = Picky::Client.new :host => 'localhost', :port => 8080, :path => '/books'
|
14
|
+
|
15
|
+
set :static, true
|
16
|
+
set :public, File.dirname(__FILE__)
|
17
|
+
set :views, File.expand_path('views', File.dirname(__FILE__))
|
19
18
|
|
20
|
-
# Root, the search
|
19
|
+
# Root, the search interface.
|
21
20
|
#
|
22
21
|
get '/' do
|
23
22
|
@query = params[:q]
|
@@ -25,41 +24,31 @@ get '/' do
|
|
25
24
|
haml :'/search'
|
26
25
|
end
|
27
26
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# You get the results from the picky server and then
|
31
|
-
# populate the result hash with rendered models.
|
27
|
+
# For full results, you get the ids from the picky server
|
28
|
+
# and then populate the result with models (rendered, even).
|
32
29
|
#
|
33
30
|
get '/search/full' do
|
34
|
-
results =
|
31
|
+
results = BooksSearch.search params[:query], :ids => params[:ids], :offset => params[:offset]
|
35
32
|
results.extend Picky::Convenience
|
36
33
|
results.populate_with Book do |book|
|
37
|
-
book.
|
34
|
+
book.to_s
|
38
35
|
end
|
39
36
|
|
40
37
|
#
|
41
|
-
# Or
|
38
|
+
# Or use:
|
42
39
|
# results.populate_with Book
|
43
40
|
#
|
44
|
-
# Then
|
41
|
+
# Then:
|
45
42
|
# rendered_entries = results.entries.map do |book| (render each book here) end
|
46
43
|
#
|
47
44
|
|
48
|
-
|
45
|
+
ActiveSupport::JSON.encode results
|
49
46
|
end
|
50
47
|
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# We don't parse/reencode the returned json string using search_unparsed.
|
48
|
+
# For live results, you'd actually go directly to the search server without taking the detour.
|
54
49
|
#
|
55
50
|
get '/search/live' do
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
# Configure. The configuration info page.
|
60
|
-
#
|
61
|
-
get '/configure' do
|
62
|
-
haml :'/configure'
|
51
|
+
BooksSearch.search_unparsed params[:query], :offset => params[:offset]
|
63
52
|
end
|
64
53
|
|
65
54
|
helpers do
|
@@ -9,34 +9,34 @@ require 'csv'
|
|
9
9
|
# * a number of subjects
|
10
10
|
#
|
11
11
|
class Book
|
12
|
-
|
12
|
+
|
13
13
|
@@books_mapping = {}
|
14
|
-
|
14
|
+
|
15
15
|
# Load the books on startup.
|
16
16
|
#
|
17
17
|
file_name = File.expand_path 'library.csv', File.dirname(__FILE__)
|
18
18
|
CSV.open(file_name, 'r').each do |row|
|
19
19
|
@@books_mapping[row.shift.to_i] = row
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# Find uses a lookup table.
|
23
23
|
#
|
24
24
|
def self.find ids, _ = {}
|
25
25
|
ids.map { |id| new(id, *@@books_mapping[id]) }
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
attr_reader :id
|
29
|
-
|
29
|
+
|
30
30
|
def initialize id, title, author, year, publisher, subjects
|
31
31
|
@id, @title, @author, @year, @publisher, @subjects = id, title, author, year, publisher, subjects
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
# "Rendering" ;)
|
35
35
|
#
|
36
36
|
# Note: This is just an example. Please do not render in the model.
|
37
37
|
#
|
38
|
-
def
|
39
|
-
"<li class='book'><
|
38
|
+
def to_s
|
39
|
+
"<li class='book'><h3><a href='http://google.com?q=#{@title}'>#{@title}</a></h3><em>#{@author}</em><p>#{@year}, #{@publisher}</p><p>#{@subjects}</p></li>"
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
end
|
Binary file
|
@@ -1,17 +1,19 @@
|
|
1
|
-
Array.prototype.index=function(
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
1
|
+
Array.prototype.index=function(a){for(var b=0,j=this.length;b<j;b++)if(this[b]==a)return b;return null};Array.prototype.include=function(a){return this.index(a)!==null};Array.prototype.remove=function(a){this.splice(a,1);return this};Array.prototype.compare=function(a){return this.join("")==a.join("")};Array.prototype.each=function(a){for(var b=0,j=this.length;b<j;b++)a(b,this[b]);return this};Array.prototype.map=function(a){for(var b=0,j=this.length;b<j;b++)this[b]=a(b,this[b]);return this};var dictionary={common:{join:{de:"und",fr:"et",it:"e",en:"and",ch:"und"},"with":{de:"mit",fr:"avec",it:"con",en:"with",ch:"mit"},of:{de:"von",fr:"de",it:"di",en:"of",ch:"vo"},to:{de:"bis",fr:"\u00e0",it:"alla",en:"to",ch:"bis"}},results:{addination:{more:{de:"Weitere Resultate",fr:"Autres r\u00e9sultats",it:"Altri risultati",en:"More results",ch:"Mee Resultaat"}},header:{de:"Ergebnisse",fr:"R\u00e9sultats",it:"Risultati",en:"Results",ch:"Erg\u00e4bnis"}}},t=function(a){for(var b=PickyI18n.locale||
|
2
|
+
"en",j=a.split(".").concat(b),i=dictionary,k=0,n=j.length;k<n;k++){i=i[j[k]];if(i==undefined){i="Translation missing: "+a+"."+b;break}}return i};function Allocation(a,b,j,i,k,n){var p=this;this.type=a;this.weight=b;this.count=j;this.combination=i;this.ids=k||[];this.entries=this.rendered=n||[];this.isType=function(s){return s==p.type}}function Allocations(a){this.allocations=[];for(var b=0,j=a.length;b<j;b++){var i=a[b];this.allocations.push(new Allocation(i[0],i[1],i[2],i[3],i[4],i[5]))}this.length=this.allocations.length;this.remove=function(k){this.allocations.splice(k,1)};this.each=function(k){this.allocations.each(k)}}
|
3
|
+
function PickyData(a){var b=a.total,j=a.duration,i=a.offset,k=new Allocations(a.allocations||[]);this.original_hash=a;this.total=b;this.duration=j;this.offset=i;this.allocations=k;this.renderedAmount=function(){var n=0;k.each(function(p,s){n+=s.rendered.length});return n};this.isEmpty=function(){return b==0}};var PickyView=function(a,b){var j=b.showResultsLimit||10,i=b.input,k=b.reset,n=b.button,p=b.counter,s=b.form,x=b.moreSelector,u=b.results,v=b.noResults,z=new PickyAddination(this,u),f=new PickyAllocationsCloud(this,b),d=new PickyResultsRenderer(z,b),g=function(){k.fadeTo(166,1)},o=function(){f.hide();u.empty();v.hide()},m=function(c){i.val(c);k.fadeTo(166,0);s.attr("class","empty");p.empty();o()};this.reset=m;var h=function(){return i.val()};this.text=h;var q=function(c){p.text(c);c>0&&c<=5&&p.fadeTo("fast",
|
4
|
+
0.5).fadeTo("fast",1)},e=function(c){if(c.isEmpty())return"none";if(c.total>j&&c.allocations.length>1)return"support";return"ok"};this.insert=function(c){i.val(c);i.select()};this.fullResultsCallback=function(c){var r=e(c);s.attr("class",r);if(c.isEmpty()){o();q(0);v.show();g()}else if(c.total>j&&c.allocations.length>1){o();g();f.show(c);q(c.total)}else if(c.offset==0){o();q(c.total);d.render(u,c);u.show();g();i.focus()}else{r=$(x).position().top;z.remove();d.render(u,c);$("body").animate({scrollTop:r-
|
5
|
+
12},500)}};this.liveResultsCallback=function(c){var r=e(c);s.attr("class",r);q(c.total)};this.allocationChosen=function(c){c=c.data.query;a.insert(c);a.allocationChosen(c)};this.addinationClicked=function(c){a.addinationClicked(h(),c)};(function(){i.keyup(function(c){if(h()==""){m();a.searchTextCleared()}else g();a.searchTextEntered(h(),c)});p.click(function(){a.searchButtonClicked(h())});n.click(function(){a.searchButtonClicked(h())});k.click(function(){m();a.clearButtonClicked();i.focus()})})();
|
6
|
+
i.focus()};var PickyBackend=function(a){var b=function(j,i,k){var n=k||{};n=$.extend({query:j},k);$.getJSON(a,n,function(p){i&&i(new PickyData(p))})};this.search=function(j,i,k,n){b(j,function(p){i&&i(n,p)},k)}},LiveBackend=function(a){var b=a.live||alert("A live backend path must be provided."),j=new PickyBackend(b);this.search=function(i,k,n,p){p=p||{};latestRequestTimestamp=new Date;p.live=latestRequestTimestamp;n=$.extend({ids:a.liveResults||0,offset:0},n);j.search(i,function(s,x){if(!s.live||s.live==latestRequestTimestamp)k&&
|
7
|
+
k(x)},n,p)}},FullBackend=function(a){var b=a.full||alert("A full backend path must be provided."),j=new PickyBackend(b);this.search=function(i,k,n,p){p=p||{};latestRequestTimestamp=new Date;p.full=latestRequestTimestamp;n=$.extend({ids:a.fullResults||20,offset:0},n);j.search(i,function(s,x){if(!s.full||s.full==latestRequestTimestamp)k&&k(x)},n,p)}};var PickyController=function(a){var b=new PickyView(this,a),j=a.backends,i=a.beforeInsert||function(){},k=a.before||function(){},n=a.success||function(){},p=a.after||function(){},s=a.liveRendered||false,x=a.liveSearchInterval||180,u,v=function(e){return(e=e&&e.match(/q=([^&]+)/))&&decodeURIComponent(e[1]).replace(/\+/g," ").replace(/#/g,"")||""};this.extractQuery=v;var z=function(){var e=window.History&&window.History.getState();return v(e&&e.url)};this.lastFullQuery=z;var f=function(e,c,r,y){var w=
|
8
|
+
k(c,y);if(w!=undefined)c=w;u=[e,c,r,y];w=c;if(w!=z()){w=w==""?"":"?q="+escape(w).replace(/\*/g,"%2A");window.History&&window.History.getState()&&window.History.pushState&&window.History.pushState(null,null,w)}if(c=="")b.reset();else(e=j[e])&&e.search(c,r,y)};this.resend=function(){u&&f.apply(this,u)};var d=function(e,c){e=n(e,c)||e;b.fullResultsCallback(e);p(e,c)},g=function(e,c){clearInterval(m);f("full",e,d,c||{})};a=function(e,c){e=n(e,c)||e;b.liveResultsCallback(e);p(e,c)};var o=s?d:a,m,h=function(){var e=
|
9
|
+
b.text();f("live",e,o,{});clearInterval(m)};m=setInterval(h,x);clearInterval(m);var q=function(e,c,r){var y=i(e);if(y!=undefined)e=y;b.insert(e);r&&g(e,c)};this.insert=q;this.clearButtonClicked=function(){clearInterval(m)};this.searchTextCleared=function(){clearInterval(m)};this.searchTextEntered=function(e,c){if($.inArray(c.keyCode,[0,8,13,32,46,48,49,50,51,52,53,54,55,56,57,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])>-1)if(c.keyCode==13)g(e);else{clearInterval(m);
|
10
|
+
m=setInterval(h,x)}};this.searchButtonClicked=function(e){g(e)};this.allocationChosen=function(e){g(e)};this.addinationClicked=function(e,c){g(e,{offset:c.data.offset})};window.History&&window.History.Adapter.bind(window,"statechange",function(){var e=window.History.getState();e=v(e.url);e!=undefined&&e!=(u&&u.length>1&&u[1])&&q(e,{},true)})};var PickyI18n={},PickyClient=function(a){PickyI18n.locale=$("html").attr("lang").split("-")[0]||"en";a.locale=a.locale||PickyI18n.locale;a.qualifiers=a.qualifiers||{};a.explanations=a.explanations||{};a.choices=a.choices||{};a.explanation_delimiters={ch:"und",de:"und",en:"and",fr:"et",it:"e"};var b=a.backends;if(b){b.live||alert("Both a full and live backend must be provided.");b.full||alert("Both a full and live backend must be provided.")}else a.backends={live:new LiveBackend(a),full:new FullBackend(a)};
|
11
|
+
b=a.enclosingSelector||".picky";var j=a.formSelector||b+" form";a.form=$(j);a.input=$(a.inputSelector||j+" input[type=search]");a.reset=$(a.resetSelector||j+" div.reset");a.button=$(a.buttonSelector||j+" input[type=button]");a.counter=$(a.counterSelector||j+" div.status");a.results=$(a.resultsSelector||b+" div.results");a.noResults=$(a.noResultsSelector||b+" div.no_results");a.moreSelector=a.moreSelector||b+" div.results div.addination:last";a.allocations=$(a.allocationsSelector||b+" .allocations");
|
12
|
+
a.shownAllocations=a.allocations.find(".shown");a.showMoreAllocations=a.allocations.find(".more");a.hiddenAllocations=a.allocations.find(".hidden");a.maxSuggestions=a.maxSuggestions||3;a.results=$(a.resultsSelector||b+" div.results");a.resultsDivider=a.resultsDivider||"";a.nonPartial=a.nonPartial||[];a.wrapResults=a.wrapResults||"<ol></ol>";var i=a.controller&&new a.controller(a)||new PickyController(a);var k=this.insert=function(n,p,s){i.insert(n,p||{},s||true)};this.resend=i.resend;this.insertFromURL=
|
13
|
+
function(n){if(n)k(n);else(n=i.lastFullQuery())&&k(n)}};var PickyAddination=function(a,b){this.remove=function(){b.find(".addination").remove()};this.render=function(j){var i=j.total,k,n=j.renderedAmount();k=j.offset+n;n=k+n;j=j.total;if(j<n)n=j;k={offset:k,start:k+1,end:n};if(k.offset<i){i=$("<div class='addination'>"+t("results.addination.more")+"</div>");i.bind("click",{offset:k.offset},a.addinationClicked);return i}else return""}};var PickyResultsRenderer=function(a,b){var j=b.locale,i=b.explanations||{},k=b.explanation_delimiters||{},n=b.resultsDivider,p=b.wrapResults,s=b.nonPartial,x=function(d){var g=d[d.length-1];if(g===undefined)return[];d=d.slice(0,d.length-1);if(d==[])d=[d];if(!s.include(g[0]))if(g[1].match(/[^\*~]$/))g[1]+="*";d.push(g);return d};this.asteriskifyLastToken=x;var u=function(d){for(var g=i[j]||{},o=[],m,h=0,q=d.length;h<q;h++){m=d[h];var e=m[0];e=g[e]||e;o.push([e,m[1]])}return o};this.explainCategory=
|
14
|
+
u;var v=function(d,g){return[d.replace(/([\w\s\u00c4\u00e4\u00d6\u00f6\u00dc\u00fc\u00e9\u00e8\u00e0\,]+)/,"<strong>$1</strong>"),g].join(" ")};this.strongify=v;var z=function(d,g){var o=k[j],m="",h=[],q=[];u(x(g)).each(function(e,c){var r=c[0],y=c[1];y=y.replace(/[\w,]+:(.+)/,"$1");if(m==""||r==m){h.push(y);m=r}else{var w=v(m,h.join(" "));h=[];h.push(y);m=r;q.push(w)}});q.push(v(m,h.join(" ")));q=q.join(" "+o+" ");return q='<span class="explanation">'+d+" "+q+"</span>"};this.explain=z;var f=function(d,
|
15
|
+
g){var o='<div class="header">';o+=z(g.type,g.combination);if(d.offset>0)o+='<div class="tothetop"><a href="#" onclick="javascript:$(\'body\').animate({scrollTop: 0}, 500);">↑</a></div>';o+="</div>";return o};this.renderHeader=f;this.render=function(d,g){g.allocations.each(function(o,m){if(m.entries.length>0){d.append(f(g,m)).append(m.entries.join(n));d.children("li").wrapAll(p)}});d.append(a.render(g))}};function AllocationRenderer(a){function b(f){var d={},g={},o={},m=[],h;h=0;for(l=f.length;h<l;h++){var q=f[h][0];if(q in d){d[q].push(f[h][1]);g[q].push(f[h][2]);m.push(h)}else{d[q]=[f[h][1]];g[q]=[f[h][2]];o[h]=q}}for(h in o){f[h][1]=d[o[h]];f[h][2]=g[o[h]]}for(h=m.length-1;h>=0;h--)f.remove(m[h]);return f}function j(f){f.map(function(d){return"%"+(d+1)+"$s"});return f.join(" ")}function i(f){if(f.length==0)return"";f=b(f);f.sort(function(e,c){return e[0]<c[0]?-1:1});for(var d=[],g=0,o=f.length;g<
|
16
|
+
o;g++)d.push(f[g][0]);var m=d.length==1,h=v[d.join(",")];if(h===undefined)h=v[d]=j(d);if(typeof h==="string"){v[d]={format:h,ignoreSingle:true};h=v[d]}var q=h.format;f.each(function(e,c){var r=c[0],y=c[1],w=c[2];w.map(function(B,A){var C=y[B];if(C.charAt(C.length-1)=="*")A+="...";return A});h.filter&&w.map(function(B,A){return h.filter(A)});if(m&&!(h&&h.ignoreSingle)){r=x[r]||r;return q=w.join(" ")+" ("+r+")"}w.map(function(B,A){return A.replace(/[\w,]+:(.+)/,"$1")});q=q.replace(RegExp("%"+
|
17
|
+
(e+1)+"\\$s","g"),w.join(" "))});return q}function k(f){for(var d=[],g=0,o=u.length;g<o;g++)d.push([]);d.push([]);g=0;for(o=f.length;g<o;g++){for(var m=f[g],h=m[0],q=false,e=0,c=u.length;e<c;e++)if(u[e].include(h)){d[e].push(m);q=true;break}q||d[d.length-1].push(m)}var r;for(f=d.length-1;f>=0;f--){r=d[f];if(r.length>0)break}r=r[r.length-1];z.include(r[0])||(r[1]=r[1].valueOf()+"*");return d}function n(f){var d=[];k(f).each(function(g,o){var m=i(o);m&&d.push(m)});return d.join(" ")}var p=a.locale,
|
18
|
+
s=a.qualifiers&&a.qualifiers[p]||{},x=a.explanations&&a.explanations[p]||{},u=a.groups||[],v=a.choices&&a.choices[p]||{},z=a.nonPartial||[];this.explanation=this.query=this.text="";this.contract=b;this.makeUpMissingFormat=j;this.rendered=i;this.groupify=k;this.querify=function(f){var d=[],g,o,m;for(m in f){g=f[m][0];g=s[g]||g;o=(o=f[m][1])||"";o=o.charAt(o.length-1)=="*"?"*":"";d[m]=g+":"+f[m][2]+o}return d.join(" ")};this.suggestify=n;this.render=function(f){return n(f.combination)}};var PickyAllocationsCloud=function(a,b){var j=b.allocations,i=b.shownAllocations,k=b.showMoreAllocations,n=b.hiddenAllocations,p=b.maxSuggestions,s=function(){j.hide()},x=function(f){s();a.allocationChosen(f)},u=new AllocationRenderer(b),v=function(f){var d=[];f.each(function(g,o){var m=u.querify(o.combination),h=u.render(o);h=$('<li><div class="text">'+h+'</div><div class="count">'+o.count+"</div></li>");h.bind("click",{query:m},x);d.push(h)});return d},z=function(f){if(f.length==0)return j.hide();
|
19
|
+
i.empty();k.hide();n.empty().hide();if(f.length>p){$.each(f.slice(0,p-1),function(d,g){i.append(g)});$.each(f.slice(p-1),function(d,g){n.append(g)});k.show()}else $.each(f,function(d,g){i.append(g)});return j.show()};k.click(function(){k.hide();n.show()});this.hide=s;this.show=function(f){z(v(f.allocations));j.show()}};
|