spoiler1.rkt (4423B)
1 #lang racket 2 3 (provide spoiler-wrapper-collapsed 4 spoiler-default 5 spoiler-alt 6 spoiler-button-default-to-alt 7 spoiler-button-alt-to-default 8 spoiler1 9 spler) 10 11 (require scribble/manual 12 scribble/core 13 scribble/decode 14 scribble/html-properties 15 hyper-literate 16 (for-syntax syntax/parse) 17 scriblib/render-cond) 18 19 (define spoiler-css 20 #" 21 .spoiler-wrapper-expanded .spoiler-default, 22 .spoiler-wrapper-expanded .spoiler-button-default-to-alt { 23 display:none; 24 } 25 .spoiler-wrapper-collapsed .spoiler-alt, 26 .spoiler-wrapper-collapsed .spoiler-button-alt-to-default { 27 display:none; 28 } 29 30 .spoiler-button-default-to-alt, 31 .spoiler-button-alt-to-default { 32 color: #2a657e; 33 } 34 ") 35 36 (define spoiler-js 37 (string->bytes/utf-8 38 #<<EOJS 39 function toggleSpoiler(e, doExpand) { 40 var expanded = function(className) { 41 return className.match(/\bspoiler-wrapper-expanded\b/); 42 }; 43 var collapsed = function(className) { 44 return className.match(/\bspoiler-wrapper-collapsed\b/); 45 }; 46 var found = function(className) { 47 return expanded(className) || collapsed(className); 48 }; 49 var wrapper = e; 50 while (e != document && e != null && ! found(e.className)) { 51 e = e.parentNode; 52 } 53 e.className = e 54 .className 55 .replace(/ */g, " ") 56 .replace(/\bspoiler-wrapper-expanded\b/, '') 57 .replace(/\bspoiler-wrapper-collapsed\b/, ''); 58 if (doExpand) { 59 e.className = e.className + " spoiler-wrapper-expanded"; 60 } else { 61 e.className = e.className + " spoiler-wrapper-collapsed"; 62 } 63 if (e.preventDefault) { e.preventDefault(); } 64 return false; 65 } 66 EOJS 67 )) 68 69 (define-syntax-rule (def-style name) 70 (define name 71 (style (symbol->string 'name) 72 (list (css-addition spoiler-css) 73 (js-addition spoiler-js) 74 (alt-tag "div"))))) 75 76 (def-style spoiler-wrapper-collapsed) 77 (def-style spoiler-default) 78 (def-style spoiler-alt) 79 80 (define (spoiler-button-default-to-alt txt) 81 (hyperlink 82 #:style (style "spoiler-button-default-to-alt" 83 (list (css-addition spoiler-css) 84 (js-addition spoiler-js) 85 (attributes 86 '([onclick . "return toggleSpoiler(this, true);"])))) 87 "#" 88 txt)) 89 90 (define (spoiler-button-alt-to-default txt) 91 (hyperlink 92 #:style (style "spoiler-button-alt-to-default" 93 (list (css-addition spoiler-css) 94 (js-addition spoiler-js) 95 (attributes 96 '([onclick . "return toggleSpoiler(this, false);"])))) 97 "#" 98 txt)) 99 100 (define (spoiler1 default button-default→alt button-alt→default alternate) 101 (nested-flow spoiler-wrapper-collapsed 102 (list 103 (paragraph (style #f '()) 104 (spoiler-button-default-to-alt button-default→alt)) 105 (nested-flow spoiler-default 106 (decode-flow default)) 107 (paragraph (style #f '()) 108 (spoiler-button-alt-to-default button-alt→default)) 109 (nested-flow spoiler-alt 110 (decode-flow alternate))))) 111 112 (define-syntax spler 113 (syntax-parser 114 [(_ name default ... #:expanded expanded ...) 115 #'(begin 116 (chunk #:save-as ck1 117 #:display-only 118 #:button 119 (cond-element 120 [html (list " " (smaller 121 (spoiler-button-default-to-alt "expand")))] 122 [else (list)]) 123 name 124 default ...) 125 126 (chunk #:save-as ck2 127 #:button 128 (cond-element 129 [html (list " " (smaller 130 (spoiler-button-alt-to-default "collapse")))] 131 [else (list)]) 132 name 133 expanded ...) 134 135 (cond-block 136 [html (nested-flow spoiler-wrapper-collapsed 137 (list (nested-flow spoiler-default 138 (decode-flow (ck1))) 139 (nested-flow spoiler-alt 140 (decode-flow (ck2)))))] 141 [else (nested-flow (style #f '()) 142 (decode-flow (ck2)))]))]))