1:- module(accordion, [accordion//2, accordion_section//2]).
7:- use_module(library(http/html_write)). 8:- use_module(library(http/html_head)). 9 10:- ensure_loaded(weblog(resources/resources)). 11 12:- html_meta accordion(, , , ). 13:- predicate_options(accordion//2, 1, [ 14 collapsible(boolean), 15 inactive(text), 16 active(text), 17 height(oneof([fill, content])), 18 hoverintent(boolean), 19 sortable(boolean) 20 ]).
Works with library(http/html_write)
Uses the JQuery library
accordion has a structural similarity to the html ordered list tag
<OL>
<LI>First Thing</LI>
<LI>Second Thing</LI>
</OL>
in that an accordion expects a list of \accordion_section//2 inclusions
Example:
thing_with_accordion -->
html([
h2('All of Acmes Fine Products'),
\accordion([sortable(true)], [
\accordion_section('Acme Products #133 Portable Hole', [
p(img(src='portablehole.png', []),
'The Best hole for the money'),
p('Not recommended for use by coyotes')
]),
...
\accordion_section('Acme Products #17 Nuclear Bomb', [
p(img(src='nuke.png', []),
'27Kiloton Nuclear Bomb'),
p('The best road runner blaster on themarket') ])
])
Options:
collapsible(true).active(atom) is required. Inactive headers will receive
the atom as an additional class.active(atom) is required. Inactive headers will receive
the atom as an additional class.height(fill). To consume only as much space as is needed for content, set height(content) (the default).
NOTE: If the containing box is resized after initial draw
$( "#accordion" ).accordion( "refresh" );
must be called. See http://jqueryui.com/accordion/#fillspace
sortable(true) is used, then each group of sections to be drug
together must be surrounded by
div(class=group, ... accordion_sections ... )
accordion ). Accordions sharing a page need unique IDs.Note: currently not implemented.
117accordion(Options, _, _, _) :- 118 option(inactive(_), Options), 119 \+ option(active(_), Options), 120 throw(error(domain_error(list, Options), context(accordion//2, 121 'inactive option demands active'))). 122accordion(Options, _, _, _) :- 123 option(active(_), Options), 124 \+ option(inactive(_), Options), 125 throw(error(domain_error(list, Options), context(accordion//2, 126 'active option demands inactive'))). 127 128accordion(Options, HTML) --> 129 { 130 debug(weblog, 'accordion got ~q: ~q', [Options, HTML] ), 131 valid_accordion_html(HTML), 132 option(id(ID), Options, accordion), 133 phrase(accordion_javascript(Options), CScript), 134 atom_codes(AScript, CScript) 135 }, 136 html([ 137 \html_requires(jquery_ui), 138 div(id=ID, HTML), 139 script(AScript) 140 ]), 141 !. 142accordion(_, HTML, _, _) :- 143 throw(error(domain_error(list, HTML), 144 context(accordion/2, 'Cannot generate HTML. Only \ 145accordion_section//2 can be direct child of accordion//2'))).
div(class=group, blahblah)152valid_accordion_html(_:X) :- 153 valid_accordion_html(X). 154 155valid_accordion_html([]). 156valid_accordion_html([\accordion_section(_, _) | T]) :- 157 valid_accordion_html(T). 158 159valid_accordion_html([\(_:accordion_section(_, _)) | T]) :- 160 valid_accordion_html(T). 161 162valid_accordion_html(\accordion_section(_, _)). 163valid_accordion_html(\(_:accordion_section(_, _))). 164 165% Allow containing divs for sort grouping 166valid_accordion_html([div(_, HTML) | T]) :- 167 valid_accordion_html(HTML), 168 valid_accordion_html(T). 169 170 171:- html_meta accordion_section(, , , ).
182accordion_section(Header, HTML) --> 183 { 184 atomic(Header) 185 }, 186 html([ 187 h3(Header), 188 div(HTML) 189 ]). 190 191:- html_meta grouped_accordion_section(, , , ).
Note - don't use this, see the sortable option in accordion//2
204grouped_accordion_section(Header, HTML) --> 205 { 206 atomic(Header) 207 }, 208 html([ 209 div(class=group, [ 210 h3(Header), 211 div(class=group, HTML) 212 ]) 213 ]). 214 215 216accordion_javascript(Options) --> 217 { 218 option(id(ID), Options, accordion), 219 atom_codes(ID, CID) 220 }, 221 jquery_call_start, 222 accordion_call_open(CID), 223 accordion_call_options(Options), 224 accordion_call_close, 225 attached_calls(Options), 226 ";\n", 227 jquery_call_end, 228 accordion_post_javascript(Options). 229 230accordion_call_open(CID) --> 231 " $( \"#", 232 , 233 "\" ).accordion({\n". 234 235accordion_call_close --> 236 "dummy: 3\n})". 237 238jquery_call_start --> 239 " $(function() {\n". 240 241jquery_call_end --> 242 " });\n". 243 244accordion_call_options(Options) --> 245 collapse_options(Options), 246 icons_options(Options), 247 fillspace_options(Options), 248 hover_options(Options), 249 sortable_options(Options). 250 251collapse_options(Options) --> 252 { 253 option(collapsible(false), Options, false) 254 }, 255 [],!. 256collapse_options(_) --> 257 "collapsible: true,\n". 258 259icons_options(Options) --> 260 { 261 \+ option(active(_), Options) 262 }, 263 [],!. 264icons_options(Options) --> 265 { 266 option(active(Active), Options), 267 option(inactive(Inactive), Options), 268 atom_codes(Active, CActive), 269 atom_codes(Inactive, CInactive) 270 }, 271 "icons: {\n header: \"", 272 , 273 "\",\n activeHeader: \"", 274 , 275 "\"\n },\n". 276 277fillspace_options(Options) --> 278 { 279 option(height(content), Options, content) 280 }, 281 "heightStyle: \"content\",\n", 282 !. 283fillspace_options(_) --> 284 "heightStyle: \"fill\",\n". 285 286hover_options(Options) --> 287 { 288 option(hover(false), Options, false) 289 }, 290 [],!. 291hover_options(_) --> 292 "event: \"click hoverintent\",\n". 293 294sortable_options(Options) --> 295 { 296 option(sortable(false), Options, false) 297 }, 298 [],!. 299sortable_options(_) --> 300 "header: \"> div > h3\",\n". 301 302attached_calls(Options) --> 303 { 304 option(sortable(false), Options, false) 305 }, 306 [],!. 307attached_calls(_) --> 308 ".sortable({\n\c 309 axis: \"y\",\n\c 310 handle: \"h3\",\n\c 311 stop: function( event, ui ) {\n\c 312 // IE doesn't register the blur when sorting\n\c 313 // so trigger focusout handlers to remove .ui-state-focus\n\c 314 ui.item.children( \"h3\" ).triggerHandler( \"focusout\" );\n\c 315 }})\n". 316 317accordion_post_javascript(Options) --> 318 hover_post_options(Options). 319 320 321hover_post_options(Options) --> 322 { 323 option(hover(false), Options, false) 324 }, 325 [],!. 326hover_post_options(_) --> 327"var cfg = ($.hoverintent = {\n\c 328 sensitivity: 7,\n\c 329 interval: 100\n\c 330 });\n\c 331 \n\c 332 $.event.special.hoverintent = { 333 setup: function() { 334 $( this ).bind( 'mouseover', jQuery.event.special.hoverintent.handler ); 335 }, 336 teardown: function() { 337 $( this ).unbind( 'mouseover', jQuery.event.special.hoverintent.handler ); 338 }, 339 handler: function( event ) { 340 var currentX, currentY, timeout, 341 args = arguments, 342 target = $( event.target ), 343 previousX = event.pageX, 344 previousY = event.pageY; 345 346 function track( event ) { 347 currentX = event.pageX; 348 currentY = event.pageY; 349 }; 350 351 function clear() { 352 target 353 .unbind( 'mousemove', track ) 354 .unbind( 'mouseout', clear ); 355 clearTimeout( timeout ); 356 } 357 358 function handler() { 359 var prop, 360 orig = event; 361 362 if ( ( Math.abs( previousX - currentX ) + 363 Math.abs( previousY - currentY ) ) < 7 ) { 364 clear(); 365 366 event = $.Event( 'hoverintent' ); 367 for ( prop in orig ) { 368 if ( !( prop in event ) ) { 369 event[ prop ] = orig[ prop ]; 370 } 371 } 372 // Prevent accessing the original event since the new event 373 // is fired asynchronously and the old event is no longer 374 // usable (#6028) 375 delete event.originalEvent; 376 377 target.trigger( event ); 378 } else { 379 previousX = currentX; 380 previousY = currentY; 381 timeout = setTimeout( handler, 100 ); 382 } 383 } 384 385 timeout = setTimeout( handler, 100 ); 386 target.bind({ 387 mousemove: track, 388 mouseout: clear 389 }); 390 } 391};\n"
Accordion widget
*/