30
31:- module(cp_simple_search,
32 [ simple_search_form//0,
33 simple_search_form//1, 34 search_filter/2
35 ]). 36:- use_module(library(http/http_json)). 37:- use_module(library(http/http_parameters)). 38:- use_module(library(http/http_path)). 39:- use_module(library(http/http_dispatch)). 40:- use_module(library(http/http_wrapper)). 41:- use_module(library(http/html_write)). 42:- use_module(library(http/html_head)). 43
44:- use_module(library(semweb/rdf_db)). 45:- use_module(library(semweb/rdfs)). 46:- use_module(library(semweb/rdf_label)). 47
48:- use_module(library(option)). 49:- use_module(components(basics)). 50
51
52:- http_handler(api(ac_find_literal), ac_find_literal, []).
83simple_search_form -->
84 simple_search_form([]).
85
86simple_search_form(Options) -->
87 { option(label(Label), Options, 'Search'),
88 option(submit_handler(Search), Options, search)
89 },
90 html(form([ id(search_form),
91 action(location_by_id(Search))
92 ],
93 [ div([ \search_box([ name(q) | Options ]),
94 \filter(Options),
95 \select_handler(Options),
96 input([ type(submit),
97 value(Label)
98 ])
99 ])
100 ])).
101
102filter(Options) -->
103 { option(filter(Filter), Options), !,
104 term_to_atom(Filter, FilterAtom)
105 },
106 hidden(filter, FilterAtom).
107filter(_) --> [].
108
109select_handler(Options) -->
110 { option(select_handler(Handler), Options) }, !,
111 hidden(handler, Handler).
112select_handler(_) --> [].
113
114
115max_results_displayed(100).
116
117search_box(Options) -->
118 { max_results_displayed(Max)
119 },
120 autocomplete(ac_find_literal,
121 [ query_delay(0.2),
122 auto_highlight(false),
123 max_results_displayed(Max),
124 width('30ex')
125 | Options
126 ]).
141autocomplete(Handler, Options) -->
142 { option(id(ID), Options, ac_find_literal),
143 atom_concat(ID, '_complete', CompleteID),
144 atom_concat(ID, '_input', InputID),
145 atom_concat(ID, '_container', ContainerID),
146 select_option(width(Width), Options, Options1, '25em'),
147 select_option(name(Name), Options1, Options2, predicate),
148 select_option(value(PValue), Options2, Options3, ''),
149 expand_value(PValue, Value)
150 },
151 html([ \html_requires(yui('autocomplete/autocomplete.js')),
152 \html_requires(yui('autocomplete/assets/skins/sam/autocomplete.css')),
153 div([ id(CompleteID),
154 class(ac_input)
155 ],
156 [ input([ id(InputID),
157 name(Name),
158 value(Value),
159 type(text)
160 ]),
161 div(id(ContainerID), [])
162 ]),
163 style(type('text/css'),
164 [ '#', CompleteID, '\n',
165 '{ width:~w; padding-bottom:0em; display:inline-block; vertical-align:top}'-[Width]
166 ]),
167 \autocomplete_script(Handler, InputID, ContainerID, Options3)
168 ]).
175expand_value(p(Name), Value) :- !,
176 ( http_current_request(Request),
177 memberchk(search(Search), Request),
178 memberchk(Name=PValue, Search)
179 -> Value = PValue
180 ; Value = ''
181 ).
182expand_value(Value, Value).
183
184
185highlight -->
186 html(script(type('text/javascript'),
187\[
188 'function highlighMatches(str, query, cls)\n',
189 '{ var pat = new RegExp(query, "gi");
190 var sa = str.split(pat);
191 var ma = str.match(pat);
192 var i;
193 var out = sa[0];\n',
194
195 ' if ( !ma )
196 { return str;
197 }\n',
198
199 ' for(i=0; i<ma.length; )
200 { out += "<span class=\'"+cls+"\'>"+ma[i++]+"</span>";
201 out += sa[i];
202 }\n',
203
204 'return out;
205 }\n'
206 ])).
207
208autocomplete_script(HandlerID, Input, Container, Options) -->
209 { http_link_to_id(HandlerID, [], Path),
210 option(filter(Filter), Options, true),
211 term_to_atom(Filter, FilterAtom),
212 uri_query_components(QS, [filter(FilterAtom)])
213 },
214 highlight,
215 html(script(type('text/javascript'), \[
216'{ \n',
217' var oDS = new YAHOO.util.XHRDataSource("~w");\n'-[Path],
218' oDS.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;\n',
219' oDS.responseSchema = { resultsList:"results",
220 fields:["label","count","href"]
221 };\n',
222' oDS.maxCacheEntries = 5;\n',
223' var oAC = new YAHOO.widget.AutoComplete("~w", "~w", oDS);\n'-[Input, Container],
224' oAC.resultTypeList = false;\n',
225' oAC.formatResult = function(oResultData, sQuery, sResultMatch) {
226 var sLabel = highlighMatches(oResultData.label, sQuery, "acmatch");
227 if ( oResultData.count > 1 ) {
228 sLabel += " <span class=\\"account\\">("+oResultData.count+")</span>";
229 }
230 return sLabel;
231 };\n',
232' oAC.itemSelectEvent.subscribe(function(sType, aArgs) {
233 var oData = aArgs[2];
234 window.location.href = oData.href;
235 });\n',
236' oAC.generateRequest = function(sQuery) {
237 return "?~w&query=" + sQuery ;
238 };\n'-[QS],
239 \ac_options(Options),
240'}\n'
241 ])).
242ac_options([]) -->
243 [].
244ac_options([H|T]) -->
245 ac_option(H),
246 ac_options(T).
247
248ac_option(query_delay(Time)) --> !,
249 html([ ' oAC.queryDelay = ~w;\n'-[Time] ]).
250ac_option(auto_highlight(Bool)) --> !,
251 html([ ' oAC.autoHighlight = ~w;\n'-[Bool] ]).
252ac_option(max_results_displayed(Max)) --> !,
253 html([ ' oAC.maxResultsDisplayed = ~w;\n'-[Max] ]).
254ac_option(_) --> [].
262ac_find_literal(Request) :-
263 max_results_displayed(DefMax),
264 http_parameters(Request,
265 [ query(Query,
266 [ description('Prefix for literals to find')
267 ]),
268 filter(FilterAtom,
269 [ optional(true),
270 description('Filter on raw matches (a Prolog term)')
271 ]),
272 handler(Handler,
273 [ default(list_triples_with_literal),
274 description('Callback handler on selection')
275 ]),
276 maxResultsDisplayed(Max,
277 [ integer, default(DefMax),
278 description('Maximum number of results displayed')
279 ])
280 ]),
281 ( var(FilterAtom)
282 -> Filter = true
283 ; atom_to_term(FilterAtom, Filter0, []),
284 rdf_global_term(Filter0, Filter)
285 ),
286 autocompletions(Query, Filter, Handler, Max, Count, Completions),
287 reply_json(json([ query = json([ count=Count
288 ]),
289 results = Completions
290 ])).
291
292autocompletions(Query, Filter, Handler, Max, Count, Completions) :-
293 autocompletions(prefix(label), Query, Filter,
294 Handler, Max, BNC, ByName),
295 ( BNC > Max
296 -> Completions = ByName,
297 Count = BNC
298 ; TMax is Max-BNC,
299 autocompletions(prefix(other), Query, Filter,
300 Handler, TMax, BTC, ByToken),
301 append(ByName, ByToken, Completions),
302 Count is BNC+BTC
303 ).
304
305autocompletions(How, Query, Filter, Handler, Max, Count, Completions) :-
306 ac_objects(How, Query, Filter, Completions0),
307 length(Completions0, Count),
308 first_n(Max, Completions0, Completions1),
309 maplist(obj_result(Handler), Completions1, Completions).
310
311obj_result(Handler, Text-Count,
312 json([ label=Text,
313 count=Count,
314 href=Href
315 ])) :-
316 object_href(Handler, Text, Href).
317
318object_href(Handler, Text, Link) :- !,
319 http_link_to_id(Handler, [ q=Text ], Link).
320
321first_n(0, _, []) :- !.
322first_n(_, [], []) :- !.
323first_n(N, [H|T0], [H|T]) :-
324 N2 is N - 1,
325 first_n(N2, T0, T).
332ac_objects(How, Query, Filter, Objects) :-
333 findall(Pair, ac_object(How, Query, Filter, Pair), Pairs),
334 keysort(Pairs, KSorted),
335 group_pairs_by_key(KSorted, Grouped),
336 maplist(hit_count, Grouped, Objects).
337
338hit_count(Text-Resources, Text-Count) :-
339 length(Resources, Count).
344ac_object(prefix(label), Query, Filter, Text-Resource) :-
345 ac_candidate(Query, Filter, Resource, P, Literal),
346 ( label_property(LP),
347 rdfs_subproperty_of(P, LP)
348 -> literal_text(Literal, Text)
349 ).
350ac_object(prefix(other), Query, Filter, Text-Resource) :-
351 ac_candidate(Query, Filter, Resource, P, Literal),
352 ( label_property(LP),
353 rdfs_subproperty_of(P, LP)
354 -> fail
355 ; literal_text(Literal, Text)
356 ).
357
358ac_candidate(Query, Filter, R, P, Literal) :-
359 ( sub_term(graph(Graph), Filter)
360 -> rdf(R, P, literal(prefix(Query), Literal), Graph)
361 ; rdf(R, P, literal(prefix(Query), Literal))
362 ),
363 search_filter(Filter, R).
374search_filter(true, _) :- !.
375search_filter(graph(_), _) :- !. 376search_filter(Filter, _) :-
377 domain_error(filter, Filter)
Simple literal search
*/