├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── JasperHelpers.html
├── JasperHelpers
│ ├── Forms.html
│ ├── Kit.html
│ ├── Links.html
│ ├── OptionHash.html
│ ├── Tags.html
│ └── Text.html
├── css
│ └── style.css
├── index.html
├── index.json
├── js
│ └── doc.js
└── search-index.js
├── shard.lock
├── shard.yml
├── spec
├── jasper_helpers
│ ├── forms_spec.cr
│ ├── kit_spec.cr
│ ├── links_spec.cr
│ ├── tags_spec.cr
│ └── text_spec.cr
└── spec_helper.cr
└── src
├── jasper_helpers.cr
└── jasper_helpers
├── forms.cr
├── kit.cr
├── links.cr
├── tags.cr
├── text.cr
└── version.cr
/.gitignore:
--------------------------------------------------------------------------------
1 | /doc/
2 | /lib/
3 | /bin/
4 | /.shards/
5 | .shard.lock
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: crystal
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Amber Team
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/https://travis-ci.org/amberframework/jasper-helpers)
2 |
3 | # Jasper::Helpers
4 |
5 | A library of helpers for [Amber Framework](https://amberframework.org).
6 |
7 | ## Installation
8 |
9 | Add this to your application's shard.yml:
10 |
11 | ```yaml
12 | dependencies:
13 | jasper_helpers:
14 | github: amberframework/jasper-helpers
15 | ```
16 |
17 | ## Usage
18 |
19 | ```crystal
20 | require "jasper_helpers"
21 |
22 | class ApplicationController < Amber::Controller::Base
23 | include JasperHelpers
24 | LAYOUT = "application.slang"
25 | end
26 | ```
27 |
28 | ## Contributing
29 |
30 | 1. Fork it ( https://github.com/amberframework/Jasper-Helpers/fork )
31 | 2. Create your feature branch (git checkout -b my-new-feature)
32 | 3. Commit your changes (git commit -am 'Add some feature')
33 | 4. Push to the branch (git push origin my-new-feature)
34 | 5. Create a new Pull Request
35 |
36 | ## Contributors
37 |
38 | - [elorest](https://github.com/elorest) Isaac Sloan - Maintainer
39 | - [skunkworker](https://github.com/skunkworker) John Bolliger Maintainer
40 |
--------------------------------------------------------------------------------
/docs/JasperHelpers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | module JasperHelpers
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
123 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
152 |
153 |
154 |
155 | jasper_helpers.cr
156 |
157 |
158 |
159 |
160 |
161 |
162 | jasper_helpers/forms.cr
163 |
164 |
165 |
166 |
167 |
168 |
169 | jasper_helpers/version.cr
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
186 |
187 |
188 |
189 |
190 | VERSION = "0.2.5"
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 | button_to(body : String, url : String, method : Symbol = :post ) button_to(body : String, url : String, method : Symbol = :post , &) button_to(body : String, url : String, method : Symbol = :post , **options : Object) button_to(body : String, url : String, method : Symbol = :post , **options : Object, &)
215 | button_to ,
216 |
217 |
218 |
219 | link_to(body : String, url : String) link_to(body : String, url : String, **options : Object) link_to(url : String, &) link_to(url : String, **options : Object, &)
220 | link_to
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | check_box(name : String | Symbol, checked_value = "1" , unchecked_value = "0" , **options : Object) check_box(name : String | Symbol, checked_value = "1" , unchecked_value = "0" )
239 | check_box ,
240 |
241 |
242 |
243 | file_field(name : String | Symbol) file_field(name : String | Symbol, **options : Object)
244 | file_field ,
245 |
246 |
247 |
248 | form(method = :post , &block) form(method = :post , **options : Object, &)
249 | form ,
250 |
251 |
252 |
253 | hidden_field(name : String | Symbol) hidden_field(name : String | Symbol, **options : Object)
254 | hidden_field ,
255 |
256 |
257 |
258 | label(name : String | Symbol, content : String? = nil ) label(name : String | Symbol, content : String? = nil , **options : Object) label(name : String | Symbol, &)
259 | label ,
260 |
261 |
262 |
263 | select_field(name : String | Symbol, collection : Array | Range, **options : Object) select_field(name : String | Symbol, collection : NamedTuple, **options : Object) select_field(name : String | Symbol, collection : Hash, **options : Object) select_field(name : String | Symbol, collection : Array(Hash), **options : Object) select_field(name : String | Symbol, collection : Array(Array), **options : Object) select_field(name : String | Symbol, collection : Array | Range) select_field(name : String | Symbol, collection : NamedTuple) select_field(name : String | Symbol, collection : Hash) select_field(name : String | Symbol, collection : Array(Hash)) select_field(name : String | Symbol, collection : Array(Array))
264 | select_field ,
265 |
266 |
267 |
268 | submit(value : String | Symbol = "Save Changes" ) submit(value : String | Symbol = "Save Changes" , **options : Object)
269 | submit ,
270 |
271 |
272 |
273 | text_area(name : String | Symbol, content : String?) text_area(name : String | Symbol, content : String?, **options : Object)
274 | text_area ,
275 |
276 |
277 |
278 | text_field(name : String | Symbol, **options : Object) text_field(name : String | Symbol)
279 | text_field ,
280 |
281 |
282 |
283 | wrapper_field(*args)
284 | wrapper_field
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
Instance methods inherited from module JasperHelpers::Text
299 |
300 |
301 |
302 | render_markdown(markdown)
303 | render_markdown
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
Instance methods inherited from module JasperHelpers::Tags
318 |
319 |
320 |
321 | content(element_name : Symbol, content : String, options : OptionHash) content(element_name : Symbol, options : OptionHash, &)
322 | content ,
323 |
324 |
325 |
326 | input_field(type : Symbol, options : OptionHash) input_field(type : Symbol, **options)
327 | input_field ,
328 |
329 |
330 |
331 | input_field_string(type : Symbol, options : OptionHash)
332 | input_field_string ,
333 |
334 |
335 |
336 | prepare_input_field_options(options : OptionHash)
337 | prepare_input_field_options
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
--------------------------------------------------------------------------------
/docs/JasperHelpers/Forms.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers::Forms - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | module JasperHelpers::Forms
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
148 |
149 | jasper_helpers/forms.cr
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
171 |
172 |
173 |
174 | #check_box (name : String | Symbol, checked_value = "1" , unchecked_value = "0" , **options : Object)
175 |
176 |
177 |
178 |
179 |
180 |
181 | #check_box (name : String | Symbol, checked_value = "1" , unchecked_value = "0" )
182 |
183 |
184 |
185 |
186 | #file_field (name : String | Symbol)
187 |
188 |
189 |
190 |
191 | #file_field (name : String | Symbol, **options : Object)
192 |
193 |
194 |
195 |
196 |
197 |
198 | #form (method = :post , &block)
199 |
200 |
201 |
202 |
203 | #form (method = :post , **options : Object, &)
204 |
205 |
206 |
207 |
208 |
209 |
210 | #hidden_field (name : String | Symbol)
211 |
212 |
213 |
214 |
215 | #hidden_field (name : String | Symbol, **options : Object)
216 |
217 |
218 |
219 |
220 |
221 |
222 | #label (name : String | Symbol, content : String? = nil )
223 |
224 |
225 |
226 |
227 | #label (name : String | Symbol, content : String? = nil , **options : Object)
228 |
229 |
230 |
231 |
232 | #label (name : String | Symbol, &)
233 |
234 |
235 |
236 |
237 | #select_field (name : String | Symbol, collection : Array | Range, **options : Object)
238 |
239 |
240 |
241 |
242 |
243 |
244 | #select_field (name : String | Symbol, collection : NamedTuple, **options : Object)
245 |
246 |
247 |
248 |
249 | #select_field (name : String | Symbol, collection : Hash, **options : Object)
250 |
251 |
252 |
253 |
254 |
255 |
256 | #select_field (name : String | Symbol, collection : Array(Hash), **options : Object)
257 |
258 | with collection Array(Hash)
259 |
260 |
261 |
262 |
263 | #select_field (name : String | Symbol, collection : Array(Array), **options : Object)
264 |
265 | select_field with collection Array(Array)
266 |
267 |
268 |
269 |
270 | #select_field (name : String | Symbol, collection : Array | Range)
271 |
272 |
273 |
274 |
275 | #select_field (name : String | Symbol, collection : NamedTuple)
276 |
277 |
278 |
279 |
280 | #select_field (name : String | Symbol, collection : Hash)
281 |
282 |
283 |
284 |
285 | #select_field (name : String | Symbol, collection : Array(Hash))
286 |
287 |
288 |
289 |
290 | #select_field (name : String | Symbol, collection : Array(Array))
291 |
292 | Utilizes method above for when options are not defined and sets class and id.
293 |
294 |
295 |
296 |
297 | #submit (value : String | Symbol = "Save Changes" )
298 |
299 |
300 |
301 |
302 | #submit (value : String | Symbol = "Save Changes" , **options : Object)
303 |
304 |
305 |
306 |
307 |
308 |
309 | #text_area (name : String | Symbol, content : String?)
310 |
311 |
312 |
313 |
314 | #text_area (name : String | Symbol, content : String?, **options : Object)
315 |
316 |
317 |
318 |
319 |
320 |
321 | #text_field (name : String | Symbol, **options : Object)
322 |
323 |
324 |
325 |
326 |
327 |
328 | #text_field (name : String | Symbol)
329 |
330 |
331 |
332 |
333 | #wrapper_field (*args)
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
360 |
361 |
362 |
363 |
364 | def
check_box (name : String | Symbol, checked_value =
"1" , unchecked_value =
"0" , **options : Object)
365 |
366 |
#
367 |
368 |
369 |
370 |
371 |
check_box
372 |
373 |
374 |
375 |
380 |
381 |
382 |
383 |
384 |
385 | def
check_box (name : String | Symbol, checked_value =
"1" , unchecked_value =
"0" )
386 |
387 |
#
388 |
389 |
390 |
391 |
396 |
397 |
398 |
399 |
400 |
401 | def
file_field (name : String | Symbol)
402 |
403 |
#
404 |
405 |
406 |
407 |
412 |
413 |
414 |
415 |
416 |
417 | def
file_field (name : String | Symbol, **options : Object)
418 |
419 |
#
420 |
421 |
422 |
423 |
424 |
file_field
425 |
426 |
427 |
428 |
433 |
434 |
435 |
436 |
437 |
438 | def
form (method =
:post , &block)
439 |
440 |
#
441 |
442 |
443 |
444 |
449 |
450 |
451 |
452 |
453 |
454 | def
form (method =
:post , **options : Object, &)
455 |
456 |
#
457 |
458 |
459 |
463 |
464 |
465 |
470 |
471 |
472 |
473 |
474 |
475 | def
hidden_field (name : String | Symbol)
476 |
477 |
#
478 |
479 |
480 |
481 |
486 |
487 |
488 |
489 |
490 |
491 | def
hidden_field (name : String | Symbol, **options : Object)
492 |
493 |
#
494 |
495 |
496 |
497 |
498 |
hidden_field
499 |
500 |
501 |
502 |
507 |
508 |
509 |
510 |
511 |
512 | def
label (name : String | Symbol, content : String? =
nil )
513 |
514 |
#
515 |
516 |
517 |
518 |
523 |
524 |
525 |
526 |
527 |
528 | def
label (name : String | Symbol, content : String? =
nil , **options : Object)
529 |
530 |
#
531 |
532 |
533 |
534 |
539 |
540 |
541 |
542 |
543 |
544 | def
label (name : String | Symbol, &)
545 |
546 |
#
547 |
548 |
549 |
550 |
555 |
556 |
557 |
558 |
559 |
560 | def
select_field (name : String | Symbol, collection : Array | Range, **options : Object)
561 |
562 |
#
563 |
564 |
565 |
566 |
567 |
with collection Array
568 |
569 |
570 |
571 |
576 |
577 |
578 |
579 |
580 |
581 | def
select_field (name : String | Symbol, collection : NamedTuple, **options : Object)
582 |
583 |
#
584 |
585 |
586 |
587 |
592 |
593 |
594 |
595 |
596 |
597 | def
select_field (name : String | Symbol, collection : Hash, **options : Object)
598 |
599 |
#
600 |
601 |
602 |
603 |
604 |
with collection Hash
605 |
606 |
607 |
608 |
613 |
614 |
615 |
616 |
617 |
618 | def
select_field (name : String | Symbol, collection : Array(Hash), **options : Object)
619 |
620 |
#
621 |
622 |
623 |
624 |
625 |
with collection Array(Hash)
626 |
627 |
628 |
629 |
634 |
635 |
636 |
637 |
638 |
639 | def
select_field (name : String | Symbol, collection : Array(Array), **options : Object)
640 |
641 |
#
642 |
643 |
644 |
645 |
646 |
select_field
647 | with collection Array(Array)
648 |
649 |
650 |
651 |
656 |
657 |
658 |
659 |
660 |
661 | def
select_field (name : String | Symbol, collection : Array | Range)
662 |
663 |
#
664 |
665 |
666 |
667 |
672 |
673 |
674 |
675 |
676 |
677 | def
select_field (name : String | Symbol, collection : NamedTuple)
678 |
679 |
#
680 |
681 |
682 |
683 |
688 |
689 |
690 |
691 |
692 |
693 | def
select_field (name : String | Symbol, collection : Hash)
694 |
695 |
#
696 |
697 |
698 |
699 |
704 |
705 |
706 |
707 |
708 |
709 | def
select_field (name : String | Symbol, collection : Array(Hash))
710 |
711 |
#
712 |
713 |
714 |
715 |
720 |
721 |
722 |
723 |
724 |
725 | def
select_field (name : String | Symbol, collection : Array(Array))
726 |
727 |
#
728 |
729 |
730 |
731 |
732 |
Utilizes method above for when options are not defined and sets class and id.
733 |
734 |
735 |
736 |
741 |
742 |
743 |
744 |
745 |
746 | def
submit (value : String | Symbol =
"Save Changes" )
747 |
748 |
#
749 |
750 |
751 |
752 |
757 |
758 |
759 |
760 |
761 |
762 | def
submit (value : String | Symbol =
"Save Changes" , **options : Object)
763 |
764 |
#
765 |
766 |
767 |
771 |
772 |
773 |
778 |
779 |
780 |
781 |
782 |
783 | def
text_area (name : String | Symbol, content : String?)
784 |
785 |
#
786 |
787 |
788 |
789 |
794 |
795 |
796 |
797 |
798 |
799 | def
text_area (name : String | Symbol, content : String?, **options : Object)
800 |
801 |
#
802 |
803 |
804 |
805 |
806 |
text_area
807 |
808 |
809 |
810 |
815 |
816 |
817 |
818 |
819 |
820 | def
text_field (name : String | Symbol, **options : Object)
821 |
822 |
#
823 |
824 |
825 |
826 |
827 |
text_field
828 |
829 |
830 |
831 |
836 |
837 |
838 |
839 |
840 |
841 | def
text_field (name : String | Symbol)
842 |
843 |
#
844 |
845 |
846 |
847 |
852 |
853 |
854 |
855 |
856 |
857 | def
wrapper_field (*args)
858 |
859 |
#
860 |
861 |
862 |
863 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
--------------------------------------------------------------------------------
/docs/JasperHelpers/Kit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers::Kit - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | module JasperHelpers::Kit
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
125 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
148 |
149 | jasper_helpers/kit.cr
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
171 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
216 |
217 |
218 |
219 |
220 | def
css_safe (value)
221 |
222 |
#
223 |
224 |
225 |
226 |
231 |
232 |
233 |
234 |
235 |
236 | def
merge (*options)
237 |
238 |
#
239 |
240 |
241 |
242 |
247 |
248 |
249 |
250 |
251 |
252 | def
safe_hash (*options)
253 |
254 |
#
255 |
256 |
257 |
258 |
263 |
264 |
265 |
266 |
267 |
268 | def
sanitize (options :
OptionHash )
269 |
270 |
#
271 |
272 |
273 |
274 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/docs/JasperHelpers/Links.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers::Links - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | module JasperHelpers::Links
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
148 |
149 | jasper_helpers/links.cr
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
171 |
172 |
173 |
174 | #button_to (body : String, url : String, method : Symbol = :post )
175 |
176 |
177 |
178 |
179 | #button_to (body : String, url : String, method : Symbol = :post , &)
180 |
181 |
182 |
183 |
184 | #button_to (body : String, url : String, method : Symbol = :post , **options : Object)
185 |
186 |
187 |
188 |
189 | #button_to (body : String, url : String, method : Symbol = :post , **options : Object, &)
190 |
191 |
192 |
193 |
194 | #link_to (body : String, url : String)
195 |
196 |
197 |
198 |
199 |
200 |
201 | #link_to (body : String, url : String, **options : Object)
202 |
203 | Creates an anchor,
, tag rendering the body
parameter as content.
204 |
205 |
206 |
207 |
208 | #link_to (url : String, &)
209 |
210 |
211 |
212 |
213 |
214 |
215 | #link_to (url : String, **options : Object, &)
216 |
217 | Creates an anchor,
, tag with the href
attribute set to the url
parameter provided.
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
244 |
245 |
246 |
247 |
248 | def
button_to (body : String, url : String, method : Symbol =
:post )
249 |
250 |
#
251 |
252 |
253 |
254 |
259 |
260 |
261 |
262 |
263 |
264 | def
button_to (body : String, url : String, method : Symbol =
:post , &)
265 |
266 |
#
267 |
268 |
269 |
270 |
275 |
276 |
277 |
278 |
279 |
280 | def
button_to (body : String, url : String, method : Symbol =
:post , **options : Object)
281 |
282 |
#
283 |
284 |
285 |
286 |
291 |
292 |
293 |
294 |
295 |
296 | def
button_to (body : String, url : String, method : Symbol =
:post , **options : Object, &)
297 |
298 |
#
299 |
300 |
301 |
302 |
307 |
308 |
309 |
310 |
311 |
312 | def
link_to (body : String, url : String)
313 |
314 |
#
315 |
316 |
317 |
318 |
319 |
:ditto:
320 |
321 |
With the exception of not accepting tag options
322 |
323 |
324 |
325 |
330 |
331 |
332 |
333 |
334 |
335 | def
link_to (body : String, url : String, **options : Object)
336 |
337 |
#
338 |
339 |
340 |
341 |
342 |
Creates an anchor,
, tag rendering the body
parameter as content. The anchor tag's href
will be set to the url
parameter provided. The options
parameter is be used to provide additional configurations or attributes for the tag.
343 |
344 |
Example:
345 |
346 |
link_to("Go to GitHub" , "http://github.com" , { id: "github-url" })
347 |
348 |
Produces:
349 |
350 |
Go to GitHub
351 |
352 |
353 |
354 |
359 |
360 |
361 |
362 |
363 |
364 | def
link_to (url : String, &)
365 |
366 |
#
367 |
368 |
369 |
370 |
371 |
:ditto:
372 |
373 |
With the exception of not accepting tag options
374 |
375 |
376 |
377 |
382 |
383 |
384 |
385 |
386 |
387 | def
link_to (url : String, **options : Object, &)
388 |
389 |
#
390 |
391 |
392 |
393 |
394 |
Creates an anchor,
, tag with the href
attribute set to the url
parameter provided.
395 | The options
parameter can be used to provide additional configurations or attributes for the tag.
396 |
397 |
Example:
398 |
399 |
link_to("http://github.com" , { id: "github-url" })
400 |
401 |
Produces:
402 |
403 |
404 |
405 |
406 |
407 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
--------------------------------------------------------------------------------
/docs/JasperHelpers/OptionHash.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers::OptionHash - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | alias JasperHelpers::OptionHash
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
121 |
Hash(Symbol, Array(Int32 | String) | Array(Int32) | Array(String) | Bool | Float32 | Float64 | Int16 | Int32 | Int64 | Int8 | Slice(UInt8) | String | Symbol | Time | Nil)
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
142 |
143 |
144 |
145 | jasper_helpers.cr
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/docs/JasperHelpers/Tags.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers::Tags - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | module JasperHelpers::Tags
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
148 |
149 | jasper_helpers/tags.cr
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
166 |
167 |
168 |
169 |
170 | INPUT_BOOLEAN_ATTRIBUTES = [:disabled ]
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
190 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
249 |
250 |
251 |
252 |
253 | def
content (element_name : Symbol, content : String, options :
OptionHash )
254 |
255 |
#
256 |
257 |
258 |
259 |
264 |
265 |
266 |
267 |
268 |
269 | def
content (element_name : Symbol, options :
OptionHash , &)
270 |
271 |
#
272 |
273 |
274 |
275 |
276 |
Builds an arbitrary HTML tag for the provided element_name
using options
to configure additional parameters for accordingly.
277 |
278 |
Example
279 |
280 |
content(:a , { aria_data_toggle: true , href: "http://example.com" })
281 |
282 |
Produces:
283 |
284 |
<a href="http://example.com", aria_data_toggle="true" />
285 |
286 |
287 |
288 |
293 |
294 |
295 |
296 |
297 |
298 | def
input_field (type : Symbol, options :
OptionHash )
299 |
300 |
#
301 |
302 |
303 |
304 |
309 |
310 |
311 |
312 |
313 |
314 | def
input_field (type : Symbol, **options)
315 |
316 |
#
317 |
318 |
319 |
320 |
325 |
326 |
327 |
328 |
329 |
330 | def
input_field_string (type : Symbol, options :
OptionHash )
331 |
332 |
#
333 |
334 |
335 |
336 |
341 |
342 |
343 |
344 |
345 |
346 | def
prepare_input_field_options (options :
OptionHash )
347 |
348 |
#
349 |
350 |
351 |
352 |
353 |
helper for input field options
354 |
355 |
356 |
357 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
--------------------------------------------------------------------------------
/docs/JasperHelpers/Text.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | JasperHelpers::Text - jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 | module JasperHelpers::Text
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
129 |
134 |
135 |
136 |
137 |
138 |
146 |
147 |
148 |
149 | jasper_helpers/text.cr
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
171 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
201 |
202 |
203 |
204 |
205 | def
render_markdown (markdown)
206 |
207 |
#
208 |
209 |
210 |
211 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | background: #FFFFFF;
3 | position: relative;
4 | margin: 0;
5 | padding: 0;
6 | width: 100%;
7 | height: 100%;
8 | overflow: hidden;
9 | }
10 |
11 | body {
12 | font-family: "Avenir", "Tahoma", "Lucida Sans", "Lucida Grande", Verdana, Arial, sans-serif;
13 | color: #333;
14 | line-height: 1.5;
15 | }
16 |
17 | a {
18 | color: #263F6C;
19 | }
20 |
21 | a:visited {
22 | color: #112750;
23 | }
24 |
25 | h1, h2, h3, h4, h5, h6 {
26 | margin: 35px 0 25px;
27 | color: #444444;
28 | }
29 |
30 | h1.type-name {
31 | color: #47266E;
32 | margin: 20px 0 30px;
33 | background-color: #F8F8F8;
34 | padding: 10px 12px;
35 | border: 1px solid #EBEBEB;
36 | border-radius: 2px;
37 | }
38 |
39 | h2 {
40 | border-bottom: 1px solid #E6E6E6;
41 | padding-bottom: 5px;
42 | }
43 |
44 | body {
45 | display: flex;
46 | }
47 |
48 | .sidebar, .main-content {
49 | overflow: auto;
50 | }
51 |
52 | .sidebar {
53 | width: 30em;
54 | color: #F8F4FD;
55 | background-color: #2E1052;
56 | padding: 0 0 30px;
57 | box-shadow: inset -3px 0 4px rgba(0,0,0,.35);
58 | line-height: 1.2;
59 | z-index: 0;
60 | }
61 |
62 | .sidebar .search-box {
63 | padding: 13px 9px;
64 | }
65 |
66 | .sidebar input {
67 | display: block;
68 | box-sizing: border-box;
69 | margin: 0;
70 | padding: 5px;
71 | font: inherit;
72 | font-family: inherit;
73 | line-height: 1.2;
74 | width: 100%;
75 | border: 0;
76 | outline: 0;
77 | border-radius: 2px;
78 | box-shadow: 0px 3px 5px rgba(0,0,0,.25);
79 | transition: box-shadow .12s;
80 | }
81 |
82 | .sidebar input:focus {
83 | box-shadow: 0px 5px 6px rgba(0,0,0,.5);
84 | }
85 |
86 | .sidebar input::-webkit-input-placeholder { /* Chrome/Opera/Safari */
87 | color: #C8C8C8;
88 | font-size: 14px;
89 | text-indent: 2px;
90 | }
91 |
92 | .sidebar input::-moz-placeholder { /* Firefox 19+ */
93 | color: #C8C8C8;
94 | font-size: 14px;
95 | text-indent: 2px;
96 | }
97 |
98 | .sidebar input:-ms-input-placeholder { /* IE 10+ */
99 | color: #C8C8C8;
100 | font-size: 14px;
101 | text-indent: 2px;
102 | }
103 |
104 | .sidebar input:-moz-placeholder { /* Firefox 18- */
105 | color: #C8C8C8;
106 | font-size: 14px;
107 | text-indent: 2px;
108 | }
109 |
110 | .project-summary {
111 | padding: 9px 15px 30px 30px;
112 | }
113 |
114 | .project-name {
115 | font-size: 1.4rem;
116 | margin: 0;
117 | color: #f4f4f4;
118 | font-weight: 600;
119 | }
120 |
121 | .project-version {
122 | margin-top: 5px;
123 | display: inline-block;
124 | position: relative;
125 | }
126 |
127 | .project-version > form::after {
128 | position: absolute;
129 | right: 0;
130 | top: 0;
131 | content: "\25BC";
132 | font-size: .6em;
133 | line-height: 1.2rem;
134 | z-index: -1;
135 | }
136 |
137 | .project-versions-nav {
138 | cursor: pointer;
139 | margin: 0;
140 | padding: 0 .9em 0 0;
141 | border: none;
142 | -moz-appearance: none;
143 | -webkit-appearance: none;
144 | appearance: none;
145 | background-color: transparent;
146 | color: inherit;
147 | font-family: inherit;
148 | font-size: inherit;
149 | line-height: inherit;
150 | }
151 | .project-versions-nav:focus {
152 | outline: none;
153 | }
154 |
155 | .project-versions-nav > option {
156 | color: initial;
157 | }
158 |
159 | .sidebar ul {
160 | margin: 0;
161 | padding: 0;
162 | list-style: none outside;
163 | }
164 |
165 | .sidebar li {
166 | display: block;
167 | position: relative;
168 | }
169 |
170 | .types-list li.hide {
171 | display: none;
172 | }
173 |
174 | .sidebar a {
175 | text-decoration: none;
176 | color: inherit;
177 | transition: color .14s;
178 | }
179 | .types-list a {
180 | display: block;
181 | padding: 5px 15px 5px 30px;
182 | }
183 |
184 | .types-list {
185 | display: block;
186 | }
187 |
188 | .sidebar a:focus {
189 | outline: 1px solid #D1B7F1;
190 | }
191 |
192 | .types-list a {
193 | padding: 5px 15px 5px 30px;
194 | }
195 |
196 | .sidebar .current > a,
197 | .sidebar a:hover {
198 | color: #866BA6;
199 | }
200 |
201 | .types-list li ul {
202 | overflow: hidden;
203 | height: 0;
204 | max-height: 0;
205 | transition: 1s ease-in-out;
206 | }
207 |
208 | .types-list li.parent {
209 | padding-left: 30px;
210 | }
211 |
212 | .types-list li.parent::before {
213 | box-sizing: border-box;
214 | content: "▼";
215 | display: block;
216 | width: 30px;
217 | height: 30px;
218 | position: absolute;
219 | top: 0;
220 | left: 0;
221 | text-align: center;
222 | color: white;
223 | font-size: 8px;
224 | line-height: 30px;
225 | transform: rotateZ(-90deg);
226 | cursor: pointer;
227 | transition: .2s linear;
228 | }
229 |
230 |
231 | .types-list li.parent > a {
232 | padding-left: 0;
233 | }
234 |
235 | .types-list li.parent.open::before {
236 | transform: rotateZ(0);
237 | }
238 |
239 | .types-list li.open > ul {
240 | height: auto;
241 | max-height: 1000em;
242 | }
243 |
244 | .main-content {
245 | padding: 0 30px 30px 30px;
246 | width: 100%;
247 | }
248 |
249 | .kind {
250 | font-size: 60%;
251 | color: #866BA6;
252 | }
253 |
254 | .superclass-hierarchy {
255 | margin: -15px 0 30px 0;
256 | padding: 0;
257 | list-style: none outside;
258 | font-size: 80%;
259 | }
260 |
261 | .superclass-hierarchy .superclass {
262 | display: inline-block;
263 | margin: 0 7px 0 0;
264 | padding: 0;
265 | }
266 |
267 | .superclass-hierarchy .superclass + .superclass::before {
268 | content: "<";
269 | margin-right: 7px;
270 | }
271 |
272 | .other-types-list li {
273 | display: inline-block;
274 | }
275 |
276 | .other-types-list,
277 | .list-summary {
278 | margin: 0 0 30px 0;
279 | padding: 0;
280 | list-style: none outside;
281 | }
282 |
283 | .entry-const {
284 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
285 | }
286 |
287 | .entry-const code {
288 | white-space: pre-wrap;
289 | }
290 |
291 | .entry-summary {
292 | padding-bottom: 4px;
293 | }
294 |
295 | .superclass-hierarchy .superclass a,
296 | .other-type a,
297 | .entry-summary .signature {
298 | padding: 4px 8px;
299 | margin-bottom: 4px;
300 | display: inline-block;
301 | background-color: #f8f8f8;
302 | color: #47266E;
303 | border: 1px solid #f0f0f0;
304 | text-decoration: none;
305 | border-radius: 3px;
306 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
307 | transition: background .15s, border-color .15s;
308 | }
309 |
310 | .superclass-hierarchy .superclass a:hover,
311 | .other-type a:hover,
312 | .entry-summary .signature:hover {
313 | background: #D5CAE3;
314 | border-color: #624288;
315 | }
316 |
317 | .entry-summary .summary {
318 | padding-left: 32px;
319 | }
320 |
321 | .entry-summary .summary p {
322 | margin: 12px 0 16px;
323 | }
324 |
325 | .entry-summary a {
326 | text-decoration: none;
327 | }
328 |
329 | .entry-detail {
330 | padding: 30px 0;
331 | }
332 |
333 | .entry-detail .signature {
334 | position: relative;
335 | padding: 5px 15px;
336 | margin-bottom: 10px;
337 | display: block;
338 | border-radius: 5px;
339 | background-color: #f8f8f8;
340 | color: #47266E;
341 | border: 1px solid #f0f0f0;
342 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
343 | transition: .2s ease-in-out;
344 | }
345 |
346 | .entry-detail:target .signature {
347 | background-color: #D5CAE3;
348 | border: 1px solid #624288;
349 | }
350 |
351 | .entry-detail .signature .method-permalink {
352 | position: absolute;
353 | top: 0;
354 | left: -35px;
355 | padding: 5px 15px;
356 | text-decoration: none;
357 | font-weight: bold;
358 | color: #624288;
359 | opacity: .4;
360 | transition: opacity .2s;
361 | }
362 |
363 | .entry-detail .signature .method-permalink:hover {
364 | opacity: 1;
365 | }
366 |
367 | .entry-detail:target .signature .method-permalink {
368 | opacity: 1;
369 | }
370 |
371 | .methods-inherited {
372 | padding-right: 10%;
373 | line-height: 1.5em;
374 | }
375 |
376 | .methods-inherited h3 {
377 | margin-bottom: 4px;
378 | }
379 |
380 | .methods-inherited a {
381 | display: inline-block;
382 | text-decoration: none;
383 | color: #47266E;
384 | }
385 |
386 | .methods-inherited a:hover {
387 | text-decoration: underline;
388 | color: #6C518B;
389 | }
390 |
391 | .methods-inherited .tooltip>span {
392 | background: #D5CAE3;
393 | padding: 4px 8px;
394 | border-radius: 3px;
395 | margin: -4px -8px;
396 | }
397 |
398 | .methods-inherited .tooltip * {
399 | color: #47266E;
400 | }
401 |
402 | pre {
403 | padding: 10px 20px;
404 | margin-top: 4px;
405 | border-radius: 3px;
406 | line-height: 1.45;
407 | overflow: auto;
408 | color: #333;
409 | background: #fdfdfd;
410 | font-size: 14px;
411 | border: 1px solid #eee;
412 | }
413 |
414 | code {
415 | font-family: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;
416 | }
417 |
418 | :not(pre) > code {
419 | background-color: rgba(40,35,30,0.05);
420 | padding: 0.2em 0.4em;
421 | font-size: 85%;
422 | border-radius: 3px;
423 | }
424 |
425 | span.flag {
426 | padding: 2px 4px 1px;
427 | border-radius: 3px;
428 | margin-right: 3px;
429 | font-size: 11px;
430 | border: 1px solid transparent;
431 | }
432 |
433 | span.flag.orange {
434 | background-color: #EE8737;
435 | color: #FCEBDD;
436 | border-color: #EB7317;
437 | }
438 |
439 | span.flag.yellow {
440 | background-color: #E4B91C;
441 | color: #FCF8E8;
442 | border-color: #B69115;
443 | }
444 |
445 | span.flag.green {
446 | background-color: #469C14;
447 | color: #E2F9D3;
448 | border-color: #34700E;
449 | }
450 |
451 | span.flag.red {
452 | background-color: #BF1919;
453 | color: #F9ECEC;
454 | border-color: #822C2C;
455 | }
456 |
457 | span.flag.purple {
458 | background-color: #2E1052;
459 | color: #ECE1F9;
460 | border-color: #1F0B37;
461 | }
462 |
463 | span.flag.lime {
464 | background-color: #a3ff00;
465 | color: #222222;
466 | border-color: #00ff1e;
467 | }
468 |
469 | .tooltip>span {
470 | position: absolute;
471 | opacity: 0;
472 | display: none;
473 | pointer-events: none;
474 | }
475 |
476 | .tooltip:hover>span {
477 | display: inline-block;
478 | opacity: 1;
479 | }
480 |
481 | .c {
482 | color: #969896;
483 | }
484 |
485 | .n {
486 | color: #0086b3;
487 | }
488 |
489 | .t {
490 | color: #0086b3;
491 | }
492 |
493 | .s {
494 | color: #183691;
495 | }
496 |
497 | .i {
498 | color: #7f5030;
499 | }
500 |
501 | .k {
502 | color: #a71d5d;
503 | }
504 |
505 | .o {
506 | color: #a71d5d;
507 | }
508 |
509 | .m {
510 | color: #795da3;
511 | }
512 |
513 | .hidden {
514 | display: none;
515 | }
516 | .search-results {
517 | font-size: 90%;
518 | line-height: 1.3;
519 | }
520 |
521 | .search-results mark {
522 | color: inherit;
523 | background: transparent;
524 | font-weight: bold;
525 | }
526 | .search-result {
527 | padding: 5px 8px 5px 5px;
528 | cursor: pointer;
529 | border-left: 5px solid transparent;
530 | transform: translateX(-3px);
531 | transition: all .2s, background-color 0s, border .02s;
532 | min-height: 3.2em;
533 | }
534 | .search-result.current {
535 | border-left-color: #ddd;
536 | background-color: rgba(200,200,200,0.4);
537 | transform: translateX(0);
538 | transition: all .2s, background-color .5s, border 0s;
539 | }
540 | .search-result.current:hover,
541 | .search-result.current:focus {
542 | border-left-color: #866BA6;
543 | }
544 | .search-result:not(.current):nth-child(2n) {
545 | background-color: rgba(255,255,255,.06);
546 | }
547 | .search-result__title {
548 | font-size: 105%;
549 | word-break: break-all;
550 | line-height: 1.1;
551 | padding: 3px 0;
552 | }
553 | .search-result__title strong {
554 | font-weight: normal;
555 | }
556 | .search-results .search-result__title > a {
557 | padding: 0;
558 | display: block;
559 | }
560 | .search-result__title > a > .args {
561 | color: #dddddd;
562 | font-weight: 300;
563 | transition: inherit;
564 | font-size: 88%;
565 | line-height: 1.2;
566 | letter-spacing: -.02em;
567 | }
568 | .search-result__title > a > .args * {
569 | color: inherit;
570 | }
571 |
572 | .search-result a,
573 | .search-result a:hover {
574 | color: inherit;
575 | }
576 | .search-result:not(.current):hover .search-result__title > a,
577 | .search-result:not(.current):focus .search-result__title > a,
578 | .search-result__title > a:focus {
579 | color: #866BA6;
580 | }
581 | .search-result:not(.current):hover .args,
582 | .search-result:not(.current):focus .args {
583 | color: #6a5a7d;
584 | }
585 |
586 | .search-result__type {
587 | color: #e8e8e8;
588 | font-weight: 300;
589 | }
590 | .search-result__doc {
591 | color: #bbbbbb;
592 | font-size: 90%;
593 | }
594 | .search-result__doc p {
595 | margin: 0;
596 | text-overflow: ellipsis;
597 | display: -webkit-box;
598 | -webkit-box-orient: vertical;
599 | -webkit-line-clamp: 2;
600 | overflow: hidden;
601 | line-height: 1.2em;
602 | max-height: 2.4em;
603 | }
604 |
605 | .js-modal-visible .modal-background {
606 | display: flex;
607 | }
608 | .main-content {
609 | position: relative;
610 | }
611 | .modal-background {
612 | position: absolute;
613 | display: none;
614 | height: 100%;
615 | width: 100%;
616 | background: rgba(120,120,120,.4);
617 | z-index: 100;
618 | align-items: center;
619 | justify-content: center;
620 | }
621 | .usage-modal {
622 | max-width: 90%;
623 | background: #fff;
624 | border: 2px solid #ccc;
625 | border-radius: 9px;
626 | padding: 5px 15px 20px;
627 | min-width: 50%;
628 | color: #555;
629 | position: relative;
630 | transform: scale(.5);
631 | transition: transform 200ms;
632 | }
633 | .js-modal-visible .usage-modal {
634 | transform: scale(1);
635 | }
636 | .usage-modal > .close-button {
637 | position: absolute;
638 | right: 15px;
639 | top: 8px;
640 | color: #aaa;
641 | font-size: 27px;
642 | cursor: pointer;
643 | }
644 | .usage-modal > .close-button:hover {
645 | text-shadow: 2px 2px 2px #ccc;
646 | color: #999;
647 | }
648 | .modal-title {
649 | margin: 0;
650 | text-align: center;
651 | font-weight: normal;
652 | color: #666;
653 | border-bottom: 2px solid #ddd;
654 | padding: 10px;
655 | }
656 | .usage-list {
657 | padding: 0;
658 | margin: 13px;
659 | }
660 | .usage-list > li {
661 | padding: 5px 2px;
662 | overflow: auto;
663 | padding-left: 100px;
664 | min-width: 12em;
665 | }
666 | .usage-modal kbd {
667 | background: #eee;
668 | border: 1px solid #ccc;
669 | border-bottom-width: 2px;
670 | border-radius: 3px;
671 | padding: 3px 8px;
672 | font-family: monospace;
673 | margin-right: 2px;
674 | display: inline-block;
675 | }
676 | .usage-key {
677 | float: left;
678 | clear: left;
679 | margin-left: -100px;
680 | margin-right: 12px;
681 | }
682 | .doc-inherited {
683 | font-weight: bold;
684 | }
685 |
686 | .anchor {
687 | float: left;
688 | padding-right: 4px;
689 | margin-left: -20px;
690 | }
691 |
692 | .main-content .anchor .octicon-link {
693 | width: 16px;
694 | height: 16px;
695 | }
696 |
697 | .main-content .anchor:focus {
698 | outline: none
699 | }
700 |
701 | .main-content h1:hover .anchor,
702 | .main-content h2:hover .anchor,
703 | .main-content h3:hover .anchor,
704 | .main-content h4:hover .anchor,
705 | .main-content h5:hover .anchor,
706 | .main-content h6:hover .anchor {
707 | text-decoration: none
708 | }
709 |
710 | .main-content h1 .octicon-link,
711 | .main-content h2 .octicon-link,
712 | .main-content h3 .octicon-link,
713 | .main-content h4 .octicon-link,
714 | .main-content h5 .octicon-link,
715 | .main-content h6 .octicon-link {
716 | visibility: hidden
717 | }
718 |
719 | .main-content h1:hover .anchor .octicon-link,
720 | .main-content h2:hover .anchor .octicon-link,
721 | .main-content h3:hover .anchor .octicon-link,
722 | .main-content h4:hover .anchor .octicon-link,
723 | .main-content h5:hover .anchor .octicon-link,
724 | .main-content h6:hover .anchor .octicon-link {
725 | visibility: visible
726 | }
727 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | jasper_helpers rs/docs
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
98 |
99 |
100 |
101 |
102 |
103 |
108 |
109 |
A library of helpers for Amber Framework .
110 |
111 |
116 |
117 |
Add this to your application's shard.yml:
118 |
119 |
dependencies:
120 | jasper_helpers:
121 | github: amberframework/jasper-helpers
122 |
123 |
128 |
129 |
require "jasper_helpers"
130 |
131 | class ApplicationController < Amber :: Controller :: Base
132 | include JasperHelpers
133 | LAYOUT = "application.slang"
134 | end
135 |
136 |
141 |
142 |
Fork it ( https://github.com/amberframework/Jasper-Helpers/fork ) Create your feature branch (git checkout -b my-new-feature) Commit your changes (git commit -am 'Add some feature') Push to the branch (git push origin my-new-feature) Create a new Pull Request
143 |
144 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/docs/js/doc.js:
--------------------------------------------------------------------------------
1 | window.CrystalDocs = (window.CrystalDocs || {});
2 |
3 | CrystalDocs.base_path = (CrystalDocs.base_path || "");
4 |
5 | CrystalDocs.searchIndex = (CrystalDocs.searchIndex || false);
6 | CrystalDocs.MAX_RESULTS_DISPLAY = 140;
7 |
8 | CrystalDocs.runQuery = function(query) {
9 | function searchType(type, query, results) {
10 | var matches = [];
11 | var matchedFields = [];
12 | var name = type.full_name;
13 | var i = name.lastIndexOf("::");
14 | if (i > 0) {
15 | name = name.substring(i + 2);
16 | }
17 | var nameMatches = query.matches(name);
18 | if (nameMatches){
19 | matches = matches.concat(nameMatches);
20 | matchedFields.push("name");
21 | }
22 |
23 | var namespaceMatches = query.matchesNamespace(type.full_name);
24 | if(namespaceMatches){
25 | matches = matches.concat(namespaceMatches);
26 | matchedFields.push("name");
27 | }
28 |
29 | var docMatches = query.matches(type.doc);
30 | if(docMatches){
31 | matches = matches.concat(docMatches);
32 | matchedFields.push("doc");
33 | }
34 | if (matches.length > 0) {
35 | results.push({
36 | id: type.id,
37 | result_type: "type",
38 | kind: type.kind,
39 | name: name,
40 | full_name: type.full_name,
41 | href: type.path,
42 | summary: type.summary,
43 | matched_fields: matchedFields,
44 | matched_terms: matches
45 | });
46 | }
47 |
48 | type.instance_methods.forEach(function(method) {
49 | searchMethod(method, type, "instance_method", query, results);
50 | })
51 | type.class_methods.forEach(function(method) {
52 | searchMethod(method, type, "class_method", query, results);
53 | })
54 | type.constructors.forEach(function(constructor) {
55 | searchMethod(constructor, type, "constructor", query, results);
56 | })
57 | type.macros.forEach(function(macro) {
58 | searchMethod(macro, type, "macro", query, results);
59 | })
60 | type.constants.forEach(function(constant){
61 | searchConstant(constant, type, query, results);
62 | });
63 |
64 | type.types.forEach(function(subtype){
65 | searchType(subtype, query, results);
66 | });
67 | };
68 |
69 | function searchMethod(method, type, kind, query, results) {
70 | var matches = [];
71 | var matchedFields = [];
72 | var nameMatches = query.matchesMethod(method.name, kind, type);
73 | if (nameMatches){
74 | matches = matches.concat(nameMatches);
75 | matchedFields.push("name");
76 | }
77 |
78 | method.args.forEach(function(arg){
79 | var argMatches = query.matches(arg.external_name);
80 | if (argMatches) {
81 | matches = matches.concat(argMatches);
82 | matchedFields.push("args");
83 | }
84 | });
85 |
86 | var docMatches = query.matches(type.doc);
87 | if(docMatches){
88 | matches = matches.concat(docMatches);
89 | matchedFields.push("doc");
90 | }
91 |
92 | if (matches.length > 0) {
93 | var typeMatches = query.matches(type.full_name);
94 | if (typeMatches) {
95 | matchedFields.push("type");
96 | matches = matches.concat(typeMatches);
97 | }
98 | results.push({
99 | id: method.id,
100 | type: type.full_name,
101 | result_type: kind,
102 | name: method.name,
103 | full_name: type.full_name + "#" + method.name,
104 | args_string: method.args_string,
105 | summary: method.summary,
106 | href: type.path + "#" + method.id,
107 | matched_fields: matchedFields,
108 | matched_terms: matches
109 | });
110 | }
111 | }
112 |
113 | function searchConstant(constant, type, query, results) {
114 | var matches = [];
115 | var matchedFields = [];
116 | var nameMatches = query.matches(constant.name);
117 | if (nameMatches){
118 | matches = matches.concat(nameMatches);
119 | matchedFields.push("name");
120 | }
121 | var docMatches = query.matches(constant.doc);
122 | if(docMatches){
123 | matches = matches.concat(docMatches);
124 | matchedFields.push("doc");
125 | }
126 | if (matches.length > 0) {
127 | var typeMatches = query.matches(type.full_name);
128 | if (typeMatches) {
129 | matchedFields.push("type");
130 | matches = matches.concat(typeMatches);
131 | }
132 | results.push({
133 | id: constant.id,
134 | type: type.full_name,
135 | result_type: "constant",
136 | name: constant.name,
137 | full_name: type.full_name + "#" + constant.name,
138 | value: constant.value,
139 | summary: constant.summary,
140 | href: type.path + "#" + constant.id,
141 | matched_fields: matchedFields,
142 | matched_terms: matches
143 | });
144 | }
145 | }
146 |
147 | var results = [];
148 | searchType(CrystalDocs.searchIndex.program, query, results);
149 | return results;
150 | };
151 |
152 | CrystalDocs.rankResults = function(results, query) {
153 | function uniqueArray(ar) {
154 | var j = {};
155 |
156 | ar.forEach(function(v) {
157 | j[v + "::" + typeof v] = v;
158 | });
159 |
160 | return Object.keys(j).map(function(v) {
161 | return j[v];
162 | });
163 | }
164 |
165 | results = results.sort(function(a, b) {
166 | var matchedTermsDiff = uniqueArray(b.matched_terms).length - uniqueArray(a.matched_terms).length;
167 | var aHasDocs = b.matched_fields.includes("doc");
168 | var bHasDocs = b.matched_fields.includes("doc");
169 |
170 | var aOnlyDocs = aHasDocs && a.matched_fields.length == 1;
171 | var bOnlyDocs = bHasDocs && b.matched_fields.length == 1;
172 |
173 | if (a.result_type == "type" && b.result_type != "type" && !aOnlyDocs) {
174 | if(CrystalDocs.DEBUG) { console.log("a is type b not"); }
175 | return -1;
176 | } else if (b.result_type == "type" && a.result_type != "type" && !bOnlyDocs) {
177 | if(CrystalDocs.DEBUG) { console.log("b is type, a not"); }
178 | return 1;
179 | }
180 | if (a.matched_fields.includes("name")) {
181 | if (b.matched_fields.includes("name")) {
182 | var a_name = (CrystalDocs.prefixForType(a.result_type) || "") + ((a.result_type == "type") ? a.full_name : a.name);
183 | var b_name = (CrystalDocs.prefixForType(b.result_type) || "") + ((b.result_type == "type") ? b.full_name : b.name);
184 | a_name = a_name.toLowerCase();
185 | b_name = b_name.toLowerCase();
186 | for(var i = 0; i < query.normalizedTerms.length; i++) {
187 | var term = query.terms[i].replace(/^::?|::?$/, "");
188 | var a_orig_index = a_name.indexOf(term);
189 | var b_orig_index = b_name.indexOf(term);
190 | if(CrystalDocs.DEBUG) { console.log("term: " + term + " a: " + a_name + " b: " + b_name); }
191 | if(CrystalDocs.DEBUG) { console.log(a_orig_index, b_orig_index, a_orig_index - b_orig_index); }
192 | if (a_orig_index >= 0) {
193 | if (b_orig_index >= 0) {
194 | if(CrystalDocs.DEBUG) { console.log("both have exact match", a_orig_index > b_orig_index ? -1 : 1); }
195 | if(a_orig_index != b_orig_index) {
196 | if(CrystalDocs.DEBUG) { console.log("both have exact match at different positions", a_orig_index > b_orig_index ? 1 : -1); }
197 | return a_orig_index > b_orig_index ? 1 : -1;
198 | }
199 | } else {
200 | if(CrystalDocs.DEBUG) { console.log("a has exact match, b not"); }
201 | return -1;
202 | }
203 | } else if (b_orig_index >= 0) {
204 | if(CrystalDocs.DEBUG) { console.log("b has exact match, a not"); }
205 | return 1;
206 | }
207 | }
208 | } else {
209 | if(CrystalDocs.DEBUG) { console.log("a has match in name, b not"); }
210 | return -1;
211 | }
212 | } else if (
213 | !a.matched_fields.includes("name") &&
214 | b.matched_fields.includes("name")
215 | ) {
216 | return 1;
217 | }
218 |
219 | if (matchedTermsDiff != 0 || (aHasDocs != bHasDocs)) {
220 | if(CrystalDocs.DEBUG) { console.log("matchedTermsDiff: " + matchedTermsDiff, aHasDocs, bHasDocs); }
221 | return matchedTermsDiff;
222 | }
223 |
224 | var matchedFieldsDiff = b.matched_fields.length - a.matched_fields.length;
225 | if (matchedFieldsDiff != 0) {
226 | if(CrystalDocs.DEBUG) { console.log("matched to different number of fields: " + matchedFieldsDiff); }
227 | return matchedFieldsDiff > 0 ? 1 : -1;
228 | }
229 |
230 | var nameCompare = a.name.localeCompare(b.name);
231 | if(nameCompare != 0){
232 | if(CrystalDocs.DEBUG) { console.log("nameCompare resulted in: " + a.name + "<=>" + b.name + ": " + nameCompare); }
233 | return nameCompare > 0 ? 1 : -1;
234 | }
235 |
236 | if(a.matched_fields.includes("args") && b.matched_fields.includes("args")) {
237 | for(var i = 0; i < query.terms.length; i++) {
238 | var term = query.terms[i];
239 | var aIndex = a.args_string.indexOf(term);
240 | var bIndex = b.args_string.indexOf(term);
241 | if(CrystalDocs.DEBUG) { console.log("index of " + term + " in args_string: " + aIndex + " - " + bIndex); }
242 | if(aIndex >= 0){
243 | if(bIndex >= 0){
244 | if(aIndex != bIndex){
245 | return aIndex > bIndex ? 1 : -1;
246 | }
247 | }else{
248 | return -1;
249 | }
250 | }else if(bIndex >= 0) {
251 | return 1;
252 | }
253 | }
254 | }
255 |
256 | return 0;
257 | });
258 |
259 | if (results.length > 1) {
260 | // if we have more than two search terms, only include results with the most matches
261 | var bestMatchedTerms = uniqueArray(results[0].matched_terms).length;
262 |
263 | results = results.filter(function(result) {
264 | return uniqueArray(result.matched_terms).length + 1 >= bestMatchedTerms;
265 | });
266 | }
267 | return results;
268 | };
269 |
270 | CrystalDocs.prefixForType = function(type) {
271 | switch (type) {
272 | case "instance_method":
273 | return "#";
274 |
275 | case "class_method":
276 | case "macro":
277 | case "constructor":
278 | return ".";
279 |
280 | default:
281 | return false;
282 | }
283 | };
284 |
285 | CrystalDocs.displaySearchResults = function(results, query) {
286 | function sanitize(html){
287 | return html.replace(/<(?!\/?code)[^>]+>/g, "");
288 | }
289 |
290 | // limit results
291 | if (results.length > CrystalDocs.MAX_RESULTS_DISPLAY) {
292 | results = results.slice(0, CrystalDocs.MAX_RESULTS_DISPLAY);
293 | }
294 |
295 | var $frag = document.createDocumentFragment();
296 | var $resultsElem = document.querySelector(".search-list");
297 | $resultsElem.innerHTML = "";
298 |
299 | results.forEach(function(result, i) {
300 | var url = CrystalDocs.base_path + result.href;
301 | var type = false;
302 |
303 | var title = query.highlight(result.result_type == "type" ? result.full_name : result.name);
304 |
305 | var prefix = CrystalDocs.prefixForType(result.result_type);
306 | if (prefix) {
307 | title = "" + prefix + " " + title;
308 | }
309 |
310 | title = "" + title + " ";
311 |
312 | if (result.args_string) {
313 | title +=
314 | "" + query.highlight(result.args_string) + " ";
315 | }
316 |
317 | $elem = document.createElement("li");
318 | $elem.className = "search-result search-result--" + result.result_type;
319 | $elem.dataset.href = url;
320 | $elem.setAttribute("title", result.full_name + " docs page");
321 |
322 | var $title = document.createElement("div");
323 | $title.setAttribute("class", "search-result__title");
324 | var $titleLink = document.createElement("a");
325 | $titleLink.setAttribute("href", url);
326 |
327 | $titleLink.innerHTML = title;
328 | $title.appendChild($titleLink);
329 | $elem.appendChild($title);
330 | $elem.addEventListener("click", function() {
331 | $titleLink.click();
332 | });
333 |
334 | if (result.result_type !== "type") {
335 | var $type = document.createElement("div");
336 | $type.setAttribute("class", "search-result__type");
337 | $type.innerHTML = query.highlight(result.type);
338 | $elem.appendChild($type);
339 | }
340 |
341 | if(result.summary){
342 | var $doc = document.createElement("div");
343 | $doc.setAttribute("class", "search-result__doc");
344 | $doc.innerHTML = query.highlight(sanitize(result.summary));
345 | $elem.appendChild($doc);
346 | }
347 |
348 | $elem.appendChild(document.createComment(JSON.stringify(result)));
349 | $frag.appendChild($elem);
350 | });
351 |
352 | $resultsElem.appendChild($frag);
353 |
354 | CrystalDocs.toggleResultsList(true);
355 | };
356 |
357 | CrystalDocs.toggleResultsList = function(visible) {
358 | if (visible) {
359 | document.querySelector(".types-list").classList.add("hidden");
360 | document.querySelector(".search-results").classList.remove("hidden");
361 | } else {
362 | document.querySelector(".types-list").classList.remove("hidden");
363 | document.querySelector(".search-results").classList.add("hidden");
364 | }
365 | };
366 |
367 | CrystalDocs.Query = function(string) {
368 | this.original = string;
369 | this.terms = string.split(/\s+/).filter(function(word) {
370 | return CrystalDocs.Query.stripModifiers(word).length > 0;
371 | });
372 |
373 | var normalized = this.terms.map(CrystalDocs.Query.normalizeTerm);
374 | this.normalizedTerms = normalized;
375 |
376 | function runMatcher(field, matcher) {
377 | if (!field) {
378 | return false;
379 | }
380 | var normalizedValue = CrystalDocs.Query.normalizeTerm(field);
381 |
382 | var matches = [];
383 | normalized.forEach(function(term) {
384 | if (matcher(normalizedValue, term)) {
385 | matches.push(term);
386 | }
387 | });
388 | return matches.length > 0 ? matches : false;
389 | }
390 |
391 | this.matches = function(field) {
392 | return runMatcher(field, function(normalized, term) {
393 | if (term[0] == "#" || term[0] == ".") {
394 | return false;
395 | }
396 | return normalized.indexOf(term) >= 0;
397 | });
398 | };
399 |
400 | function namespaceMatcher(normalized, term){
401 | var i = term.indexOf(":");
402 | if(i >= 0){
403 | term = term.replace(/^::?|::?$/, "");
404 | var index = normalized.indexOf(term);
405 | if((index == 0) || (index > 0 && normalized[index-1] == ":")){
406 | return true;
407 | }
408 | }
409 | return false;
410 | }
411 | this.matchesMethod = function(name, kind, type) {
412 | return runMatcher(name, function(normalized, term) {
413 | var i = term.indexOf("#");
414 | if(i >= 0){
415 | if (kind != "instance_method") {
416 | return false;
417 | }
418 | }else{
419 | i = term.indexOf(".");
420 | if(i >= 0){
421 | if (kind != "class_method" && kind != "macro" && kind != "constructor") {
422 | return false;
423 | }
424 | }else{
425 | //neither # nor .
426 | if(term.indexOf(":") && namespaceMatcher(normalized, term)){
427 | return true;
428 | }
429 | }
430 | }
431 |
432 | var methodName = term;
433 | if(i >= 0){
434 | var termType = term.substring(0, i);
435 | methodName = term.substring(i+1);
436 |
437 | if(termType != "") {
438 | if(CrystalDocs.Query.normalizeTerm(type.full_name).indexOf(termType) < 0){
439 | return false;
440 | }
441 | }
442 | }
443 | return normalized.indexOf(methodName) >= 0;
444 | });
445 | };
446 |
447 | this.matchesNamespace = function(namespace){
448 | return runMatcher(namespace, namespaceMatcher);
449 | };
450 |
451 | this.highlight = function(string) {
452 | if (typeof string == "undefined") {
453 | return "";
454 | }
455 | function escapeRegExp(s) {
456 | return s.replace(/[.*+?\^${}()|\[\]\\]/g, "\\$&").replace(/^[#\.:]+/, "");
457 | }
458 | return string.replace(
459 | new RegExp("(" + this.normalizedTerms.map(escapeRegExp).join("|") + ")", "gi"),
460 | "$1 "
461 | );
462 | };
463 | };
464 | CrystalDocs.Query.normalizeTerm = function(term) {
465 | return term.toLowerCase();
466 | };
467 | CrystalDocs.Query.stripModifiers = function(term) {
468 | switch (term[0]) {
469 | case "#":
470 | case ".":
471 | case ":":
472 | return term.substr(1);
473 |
474 | default:
475 | return term;
476 | }
477 | }
478 |
479 | CrystalDocs.search = function(string) {
480 | if(!CrystalDocs.searchIndex) {
481 | console.log("CrystalDocs search index not initialized, delaying search");
482 |
483 | document.addEventListener("CrystalDocs:loaded", function listener(){
484 | document.removeEventListener("CrystalDocs:loaded", listener);
485 | CrystalDocs.search(string);
486 | });
487 | return;
488 | }
489 |
490 | document.dispatchEvent(new Event("CrystalDocs:searchStarted"));
491 |
492 | var query = new CrystalDocs.Query(string);
493 | var results = CrystalDocs.runQuery(query);
494 | results = CrystalDocs.rankResults(results, query);
495 | CrystalDocs.displaySearchResults(results, query);
496 |
497 | document.dispatchEvent(new Event("CrystalDocs:searchPerformed"));
498 | };
499 |
500 | CrystalDocs.initializeIndex = function(data) {
501 | CrystalDocs.searchIndex = data;
502 |
503 | document.dispatchEvent(new Event("CrystalDocs:loaded"));
504 | };
505 |
506 | CrystalDocs.loadIndex = function() {
507 | function loadJSON(file, callback) {
508 | var xobj = new XMLHttpRequest();
509 | xobj.overrideMimeType("application/json");
510 | xobj.open("GET", file, true);
511 | xobj.onreadystatechange = function() {
512 | if (xobj.readyState == 4 && xobj.status == "200") {
513 | callback(xobj.responseText);
514 | }
515 | };
516 | xobj.send(null);
517 | }
518 |
519 | function loadScript(file) {
520 | script = document.createElement("script");
521 | script.src = file;
522 | document.body.appendChild(script);
523 | }
524 |
525 | function parseJSON(json) {
526 | CrystalDocs.initializeIndex(JSON.parse(json));
527 | }
528 |
529 | for(var i = 0; i < document.scripts.length; i++){
530 | var script = document.scripts[i];
531 | if (script.src && script.src.indexOf("js/doc.js") >= 0) {
532 | if (script.src.indexOf("file://") == 0) {
533 | // We need to support JSONP files for the search to work on local file system.
534 | var jsonPath = script.src.replace("js/doc.js", "search-index.js");
535 | loadScript(jsonPath);
536 | return;
537 | } else {
538 | var jsonPath = script.src.replace("js/doc.js", "index.json");
539 | loadJSON(jsonPath, parseJSON);
540 | return;
541 | }
542 | }
543 | }
544 | console.error("Could not find location of js/doc.js");
545 | };
546 |
547 | // Callback for jsonp
548 | function crystal_doc_search_index_callback(data) {
549 | CrystalDocs.initializeIndex(data);
550 | }
551 |
552 | Navigator = function(sidebar, searchInput, list, leaveSearchScope){
553 | this.list = list;
554 | var self = this;
555 |
556 | var performingSearch = false;
557 |
558 | document.addEventListener('CrystalDocs:searchStarted', function(){
559 | performingSearch = true;
560 | });
561 | document.addEventListener('CrystalDocs:searchDebounceStarted', function(){
562 | performingSearch = true;
563 | });
564 | document.addEventListener('CrystalDocs:searchPerformed', function(){
565 | performingSearch = false;
566 | });
567 | document.addEventListener('CrystalDocs:searchDebounceStopped', function(event){
568 | performingSearch = false;
569 | });
570 |
571 | function delayWhileSearching(callback) {
572 | if(performingSearch){
573 | document.addEventListener('CrystalDocs:searchPerformed', function listener(){
574 | document.removeEventListener('CrystalDocs:searchPerformed', listener);
575 |
576 | // add some delay to let search results display kick in
577 | setTimeout(callback, 100);
578 | });
579 | }else{
580 | callback();
581 | }
582 | }
583 |
584 | function clearMoveTimeout() {
585 | clearTimeout(self.moveTimeout);
586 | self.moveTimeout = null;
587 | }
588 |
589 | function startMoveTimeout(upwards){
590 | /*if(self.moveTimeout) {
591 | clearMoveTimeout();
592 | }
593 |
594 | var go = function() {
595 | if (!self.moveTimeout) return;
596 | self.move(upwards);
597 | self.moveTimout = setTimeout(go, 600);
598 | };
599 | self.moveTimeout = setTimeout(go, 800);*/
600 | }
601 |
602 | function scrollCenter(element) {
603 | var rect = element.getBoundingClientRect();
604 | var middle = sidebar.clientHeight / 2;
605 | sidebar.scrollTop += rect.top + rect.height / 2 - middle;
606 | }
607 |
608 | var move = this.move = function(upwards){
609 | if(!this.current){
610 | this.highlightFirst();
611 | return true;
612 | }
613 | var next = upwards ? this.current.previousElementSibling : this.current.nextElementSibling;
614 | if(next && next.classList) {
615 | this.highlight(next);
616 | scrollCenter(next);
617 | return true;
618 | }
619 | return false;
620 | };
621 |
622 | this.moveRight = function(){
623 | };
624 | this.moveLeft = function(){
625 | };
626 |
627 | this.highlight = function(elem) {
628 | if(!elem){
629 | return;
630 | }
631 | this.removeHighlight();
632 |
633 | this.current = elem;
634 | this.current.classList.add("current");
635 | };
636 |
637 | this.highlightFirst = function(){
638 | this.highlight(this.list.querySelector('li:first-child'));
639 | };
640 |
641 | this.removeHighlight = function() {
642 | if(this.current){
643 | this.current.classList.remove("current");
644 | }
645 | this.current = null;
646 | }
647 |
648 | this.openSelectedResult = function() {
649 | if(this.current) {
650 | this.current.click();
651 | }
652 | }
653 |
654 | this.focus = function() {
655 | searchInput.focus();
656 | searchInput.select();
657 | this.highlightFirst();
658 | }
659 |
660 | function handleKeyUp(event) {
661 | switch(event.key) {
662 | case "ArrowUp":
663 | case "ArrowDown":
664 | case "i":
665 | case "j":
666 | case "k":
667 | case "l":
668 | case "c":
669 | case "h":
670 | case "t":
671 | case "n":
672 | event.stopPropagation();
673 | clearMoveTimeout();
674 | }
675 | }
676 |
677 | function handleKeyDown(event) {
678 | switch(event.key) {
679 | case "Enter":
680 | event.stopPropagation();
681 | event.preventDefault();
682 | leaveSearchScope();
683 | self.openSelectedResult();
684 | break;
685 | case "Escape":
686 | event.stopPropagation();
687 | event.preventDefault();
688 | leaveSearchScope();
689 | break;
690 | case "j":
691 | case "c":
692 | case "ArrowUp":
693 | if(event.ctrlKey || event.key == "ArrowUp") {
694 | event.stopPropagation();
695 | self.move(true);
696 | startMoveTimeout(true);
697 | }
698 | break;
699 | case "k":
700 | case "h":
701 | case "ArrowDown":
702 | if(event.ctrlKey || event.key == "ArrowDown") {
703 | event.stopPropagation();
704 | self.move(false);
705 | startMoveTimeout(false);
706 | }
707 | break;
708 | case "k":
709 | case "t":
710 | case "ArrowLeft":
711 | if(event.ctrlKey || event.key == "ArrowLeft") {
712 | event.stopPropagation();
713 | self.moveLeft();
714 | }
715 | break;
716 | case "l":
717 | case "n":
718 | case "ArrowRight":
719 | if(event.ctrlKey || event.key == "ArrowRight") {
720 | event.stopPropagation();
721 | self.moveRight();
722 | }
723 | break;
724 | }
725 | }
726 |
727 | function handleInputKeyUp(event) {
728 | switch(event.key) {
729 | case "ArrowUp":
730 | case "ArrowDown":
731 | event.stopPropagation();
732 | event.preventDefault();
733 | clearMoveTimeout();
734 | }
735 | }
736 |
737 | function handleInputKeyDown(event) {
738 | switch(event.key) {
739 | case "Enter":
740 | event.stopPropagation();
741 | event.preventDefault();
742 | delayWhileSearching(function(){
743 | self.openSelectedResult();
744 | leaveSearchScope();
745 | });
746 | break;
747 | case "Escape":
748 | event.stopPropagation();
749 | event.preventDefault();
750 | // remove focus from search input
751 | leaveSearchScope();
752 | sidebar.focus();
753 | break;
754 | case "ArrowUp":
755 | event.stopPropagation();
756 | event.preventDefault();
757 | self.move(true);
758 | startMoveTimeout(true);
759 | break;
760 |
761 | case "ArrowDown":
762 | event.stopPropagation();
763 | event.preventDefault();
764 | self.move(false);
765 | startMoveTimeout(false);
766 | break;
767 | }
768 | }
769 |
770 | sidebar.tabIndex = 100; // set tabIndex to enable keylistener
771 | sidebar.addEventListener('keyup', function(event) {
772 | handleKeyUp(event);
773 | });
774 | sidebar.addEventListener('keydown', function(event) {
775 | handleKeyDown(event);
776 | });
777 | searchInput.addEventListener('keydown', function(event) {
778 | handleInputKeyDown(event);
779 | });
780 | searchInput.addEventListener('keyup', function(event) {
781 | handleInputKeyUp(event);
782 | });
783 | this.move();
784 | };
785 |
786 | CrystalDocs.initializeVersions = function () {
787 | function loadJSON(file, callback) {
788 | var xobj = new XMLHttpRequest();
789 | xobj.overrideMimeType("application/json");
790 | xobj.open("GET", file, true);
791 | xobj.onreadystatechange = function() {
792 | if (xobj.readyState == 4 && xobj.status == "200") {
793 | callback(xobj.responseText);
794 | }
795 | };
796 | xobj.send(null);
797 | }
798 |
799 | function parseJSON(json) {
800 | CrystalDocs.loadConfig(JSON.parse(json));
801 | }
802 |
803 | $elem = document.querySelector("html > head > meta[name=\"crystal_docs.json_config_url\"]")
804 | if ($elem == undefined) {
805 | return
806 | }
807 | jsonURL = $elem.getAttribute("content")
808 | if (jsonURL && jsonURL != "") {
809 | loadJSON(jsonURL, parseJSON);
810 | }
811 | }
812 |
813 | CrystalDocs.loadConfig = function (config) {
814 | var projectVersions = config["versions"]
815 | var currentVersion = document.querySelector("html > head > meta[name=\"crystal_docs.project_version\"]").getAttribute("content")
816 |
817 | var currentVersionInList = projectVersions.find(function (element) {
818 | return element.name == currentVersion
819 | })
820 |
821 | if (!currentVersionInList) {
822 | projectVersions.unshift({ name: currentVersion, url: '#' })
823 | }
824 |
825 | $version = document.querySelector(".project-summary > .project-version")
826 | $version.innerHTML = ""
827 |
828 | $select = document.createElement("select")
829 | $select.classList.add("project-versions-nav")
830 | $select.addEventListener("change", function () {
831 | window.location.href = this.value
832 | })
833 | projectVersions.forEach(function (version) {
834 | $item = document.createElement("option")
835 | $item.setAttribute("value", version.url)
836 | $item.append(document.createTextNode(version.name))
837 |
838 | if (version.name == currentVersion) {
839 | $item.setAttribute("selected", true)
840 | $item.setAttribute("disabled", true)
841 | }
842 | $select.append($item)
843 | });
844 | $form = document.createElement("form")
845 | $form.setAttribute("autocomplete", "off")
846 | $form.append($select)
847 | $version.append($form)
848 | }
849 |
850 | document.addEventListener("DOMContentLoaded", function () {
851 | CrystalDocs.initializeVersions()
852 | })
853 |
854 | var UsageModal = function(title, content) {
855 | var $body = document.body;
856 | var self = this;
857 | var $modalBackground = document.createElement("div");
858 | $modalBackground.classList.add("modal-background");
859 | var $usageModal = document.createElement("div");
860 | $usageModal.classList.add("usage-modal");
861 | $modalBackground.appendChild($usageModal);
862 | var $title = document.createElement("h3");
863 | $title.classList.add("modal-title");
864 | $title.innerHTML = title
865 | $usageModal.appendChild($title);
866 | var $closeButton = document.createElement("span");
867 | $closeButton.classList.add("close-button");
868 | $closeButton.setAttribute("title", "Close modal");
869 | $closeButton.innerText = '×';
870 | $usageModal.appendChild($closeButton);
871 | $usageModal.insertAdjacentHTML("beforeend", content);
872 |
873 | $modalBackground.addEventListener('click', function(event) {
874 | var element = event.target || event.srcElement;
875 |
876 | if(element == $modalBackground) {
877 | self.hide();
878 | }
879 | });
880 | $closeButton.addEventListener('click', function(event) {
881 | self.hide();
882 | });
883 |
884 | $body.insertAdjacentElement('beforeend', $modalBackground);
885 |
886 | this.show = function(){
887 | $body.classList.add("js-modal-visible");
888 | };
889 | this.hide = function(){
890 | $body.classList.remove("js-modal-visible");
891 | };
892 | this.isVisible = function(){
893 | return $body.classList.contains("js-modal-visible");
894 | }
895 | }
896 |
897 |
898 | document.addEventListener('DOMContentLoaded', function() {
899 | var sessionStorage;
900 | try {
901 | sessionStorage = window.sessionStorage;
902 | } catch (e) { }
903 | if(!sessionStorage) {
904 | sessionStorage = {
905 | setItem: function() {},
906 | getItem: function() {},
907 | removeItem: function() {}
908 | };
909 | }
910 |
911 | var repositoryName = document.querySelector('[name=repository-name]').getAttribute('content');
912 | var typesList = document.querySelector('.types-list');
913 | var searchInput = document.querySelector('.search-input');
914 | var parents = document.querySelectorAll('.types-list li.parent');
915 |
916 | var scrollSidebarToOpenType = function(){
917 | var openTypes = typesList.querySelectorAll('.current');
918 | if (openTypes.length > 0) {
919 | var lastOpenType = openTypes[openTypes.length - 1];
920 | lastOpenType.scrollIntoView();
921 | }
922 | }
923 |
924 | scrollSidebarToOpenType();
925 |
926 | var setPersistentSearchQuery = function(value){
927 | sessionStorage.setItem(repositoryName + '::search-input:value', value);
928 | }
929 |
930 | for(var i = 0; i < parents.length; i++) {
931 | var _parent = parents[i];
932 | _parent.addEventListener('click', function(e) {
933 | e.stopPropagation();
934 |
935 | if(e.target.tagName.toLowerCase() == 'li') {
936 | if(e.target.className.match(/open/)) {
937 | sessionStorage.removeItem(e.target.getAttribute('data-id'));
938 | e.target.className = e.target.className.replace(/ +open/g, '');
939 | } else {
940 | sessionStorage.setItem(e.target.getAttribute('data-id'), '1');
941 | if(e.target.className.indexOf('open') == -1) {
942 | e.target.className += ' open';
943 | }
944 | }
945 | }
946 | });
947 |
948 | if(sessionStorage.getItem(_parent.getAttribute('data-id')) == '1') {
949 | _parent.className += ' open';
950 | }
951 | }
952 |
953 | var leaveSearchScope = function(){
954 | CrystalDocs.toggleResultsList(false);
955 | window.focus();
956 | }
957 |
958 | var navigator = new Navigator(document.querySelector('.types-list'), searchInput, document.querySelector(".search-results"), leaveSearchScope);
959 |
960 | CrystalDocs.loadIndex();
961 | var searchTimeout;
962 | var lastSearchText = false;
963 | var performSearch = function() {
964 | document.dispatchEvent(new Event("CrystalDocs:searchDebounceStarted"));
965 |
966 | clearTimeout(searchTimeout);
967 | searchTimeout = setTimeout(function() {
968 | var text = searchInput.value;
969 |
970 | if(text == "") {
971 | CrystalDocs.toggleResultsList(false);
972 | }else if(text == lastSearchText){
973 | document.dispatchEvent(new Event("CrystalDocs:searchDebounceStopped"));
974 | }else{
975 | CrystalDocs.search(text);
976 | navigator.highlightFirst();
977 | searchInput.focus();
978 | }
979 | lastSearchText = text;
980 | setPersistentSearchQuery(text);
981 | }, 200);
982 | };
983 |
984 | if(location.hash.length > 3 && location.hash.substring(0,3) == "#q="){
985 | // allows directly linking a search query which is then executed on the client
986 | // this comes handy for establishing a custom browser search engine with https://crystal-lang.org/api/#q=%s as a search URL
987 | // TODO: Add OpenSearch description
988 | var searchQuery = location.hash.substring(3);
989 | history.pushState({searchQuery: searchQuery}, "Search for " + searchQuery, location.href.replace(/#q=.*/, ""));
990 | searchInput.value = searchQuery;
991 | document.addEventListener('CrystalDocs:loaded', performSearch);
992 | }
993 |
994 | if (searchInput.value.length == 0) {
995 | var searchText = sessionStorage.getItem(repositoryName + '::search-input:value');
996 | if(searchText){
997 | searchInput.value = searchText;
998 | }
999 | }
1000 | searchInput.addEventListener('keyup', performSearch);
1001 | searchInput.addEventListener('input', performSearch);
1002 |
1003 | var usageModal = new UsageModal('Keyboard Shortcuts', '' +
1004 | '' +
1005 | ' ' +
1006 | ' ' +
1007 | ' s ,' +
1008 | ' / ' +
1009 | ' ' +
1010 | ' Search' +
1011 | ' ' +
1012 | ' ' +
1013 | ' Esc ' +
1014 | ' Abort search / Close modal' +
1015 | ' ' +
1016 | ' ' +
1017 | ' ' +
1018 | ' ⇨ ,' +
1019 | ' Enter ' +
1020 | ' ' +
1021 | ' Open highlighted result' +
1022 | ' ' +
1023 | ' ' +
1024 | ' ' +
1025 | ' ⇧ ,' +
1026 | ' Ctrl+j ' +
1027 | ' ' +
1028 | ' Select previous result' +
1029 | ' ' +
1030 | ' ' +
1031 | ' ' +
1032 | ' ⇩ ,' +
1033 | ' Ctrl+k ' +
1034 | ' ' +
1035 | ' Select next result' +
1036 | ' ' +
1037 | ' ' +
1038 | ' ? ' +
1039 | ' Show usage info' +
1040 | ' ' +
1041 | ' '
1042 | );
1043 |
1044 | function handleShortkeys(event) {
1045 | var element = event.target || event.srcElement;
1046 |
1047 | if(element.tagName == "INPUT" || element.tagName == "TEXTAREA" || element.parentElement.tagName == "TEXTAREA"){
1048 | return;
1049 | }
1050 |
1051 | switch(event.key) {
1052 | case "?":
1053 | usageModal.show();
1054 | break;
1055 |
1056 | case "Escape":
1057 | usageModal.hide();
1058 | break;
1059 |
1060 | case "s":
1061 | case "/":
1062 | if(usageModal.isVisible()) {
1063 | return;
1064 | }
1065 | event.stopPropagation();
1066 | navigator.focus();
1067 | performSearch();
1068 | break;
1069 | }
1070 | }
1071 |
1072 | document.addEventListener('keyup', handleShortkeys);
1073 |
1074 | var scrollToEntryFromLocationHash = function() {
1075 | var hash = window.location.hash;
1076 | if (hash) {
1077 | var targetAnchor = decodeURI(hash.substr(1));
1078 | var targetEl = document.getElementById(targetAnchor)
1079 | if (targetEl) {
1080 | targetEl.offsetParent.scrollTop = targetEl.offsetTop;
1081 | }
1082 | }
1083 | };
1084 | window.addEventListener("hashchange", scrollToEntryFromLocationHash, false);
1085 | scrollToEntryFromLocationHash();
1086 | });
1087 |
--------------------------------------------------------------------------------
/shard.lock:
--------------------------------------------------------------------------------
1 | version: 2.0
2 | shards:
3 | markd:
4 | git: https://github.com/icyleaf/markd.git
5 | version: 0.4.0
6 |
7 |
--------------------------------------------------------------------------------
/shard.yml:
--------------------------------------------------------------------------------
1 | name: jasper_helpers
2 | version: 1.2.2
3 |
4 | authors:
5 | - Dru Jensen
6 | - Isaac Sloan
7 |
8 | dependencies:
9 | markd:
10 | github: icyleaf/markd
11 | version: ~> 0.4.0
12 |
13 | crystal: ">= 0.31.1, < 2.0"
14 |
15 | license: MIT
16 |
--------------------------------------------------------------------------------
/spec/jasper_helpers/forms_spec.cr:
--------------------------------------------------------------------------------
1 | require "../spec_helper"
2 |
3 | describe JasperHelpers::Forms do
4 | describe "#text_field" do
5 | it "main param works with string" do
6 | expected = " "
7 | text_field("my-great-text-input").should eq(expected)
8 | end
9 |
10 | it "main param works with symbol" do
11 | text_field(:name).should eq(" ")
12 | end
13 |
14 | it "input type with symbol works" do
15 | text_field(:name, type: :password).should eq(" ")
16 | end
17 |
18 | it "style value works" do
19 | text_field(:name, style: "color: white;").should eq(" ")
20 | end
21 | end
22 |
23 | describe "#file_field" do
24 | it "main param works with string" do
25 | expected = " "
26 | file_field("my-great-file-input").should eq(expected)
27 | end
28 |
29 | it "main param works with symbol" do
30 | file_field(:name).should eq(" ")
31 | end
32 |
33 | it "style value works" do
34 | file_field(:name, style: "color: white;").should eq(" ")
35 | end
36 | end
37 |
38 | describe "#wrapper_field" do
39 | it "creates with nil" do
40 | wrapper_field(nil).should eq("")
41 | end
42 |
43 | it "creates with string" do
44 | wrapper_field(" ").should eq(" ")
45 | end
46 |
47 | it "creates with element" do
48 | expected = " "
49 |
50 | wrapper_field(text_field("my-great-text-input")).should eq(expected)
51 | end
52 |
53 | it "creates with string and element" do
54 | expected = " "
55 |
56 | wrapper_field(
57 | " ",
58 | text_field("my-great-text-input")
59 | ).should eq(expected)
60 | end
61 | end
62 |
63 | describe "#label" do
64 | it "creates with string" do
65 | label("name", "My Label").should eq("My Label ")
66 | end
67 |
68 | it "creates with symbol" do
69 | label(:name, "My Label").should eq("My Label ")
70 | end
71 |
72 | it "creates with string and options" do
73 | label(:name, "My Label", class: "label").should eq("My Label ")
74 | end
75 |
76 | it "creates with content" do
77 | expected = " "
78 | label(:name) do
79 | check_box(:allowed)
80 | end.should eq(expected)
81 | end
82 |
83 | it "creates with wrapper_field" do
84 | expected = "name "
85 | label(:name) do
86 | wrapper_field(
87 | "name",
88 | check_box(:allowed)
89 | )
90 | end.should eq(expected)
91 | end
92 | end
93 |
94 | describe "#form" do
95 | it "allows for nested input fields" do
96 | result = form(id: "myForm") do
97 | text_field(:name)
98 | end
99 | expected = ""
100 |
101 | result.should eq(expected)
102 | end
103 |
104 | it "sets up form for multipart" do
105 | result = form(method: :post, action: "/test/1", id: "myForm", multipart: true) do
106 | text_field(:name)
107 | end
108 | expected = %()
109 |
110 | result.should eq(expected)
111 | end
112 | end
113 |
114 | describe "#hidden_field" do
115 | it "creates a hidden field" do
116 | hidden_field(:token).should eq(" ")
117 | end
118 | end
119 |
120 | describe "#select_field" do
121 | it "creates a select_field with two dimension arrays" do
122 | select_field(:age, [[1, "A"], [2, "B"]]).should eq("A B ")
123 | end
124 |
125 | it "creates a select_field with array of hashes" do
126 | select_field(:age, [{:"1" => "A"}, {:"2" => "B"}]).should eq("A B ")
127 | end
128 |
129 | it "creates a select_field with a hash" do
130 | select_field(:age, {:"1" => "A", :"2" => "B"}).should eq("A B ")
131 | end
132 |
133 | it "creates a select_field with a hash and id" do
134 | expected = "A B "
135 | select_field(:age, {:"1" => "A", :"2" => "B"}, id: "age_of_thing").should eq(expected)
136 | end
137 |
138 | it "creates a select_field with a named tuple" do
139 | select_field(:age, {"1": "A", "2": "B"}).should eq("A B ")
140 | end
141 |
142 | it "creates a select_field with a namedtuple and id" do
143 | expected = "A B "
144 | select_field(:age, {"1": "A", "2": "B"}, id: "age_of_thing").should eq(expected)
145 | end
146 |
147 | it "creates a select_field with B selected (String scalar)" do
148 | expected = "A B "
149 | select_field(:age, {"1": "A", "2": "B"}, selected: "2").should eq(expected)
150 | end
151 |
152 | it "creates a select_field with B selected (Int32 scalar)" do
153 | expected = "A B "
154 | select_field(:age, {"1": "A", "2": "B"}, selected: 2).should eq(expected)
155 | end
156 |
157 | it "creates a select_field with B and C selected (String array)" do
158 | expected = "A B C "
159 | select_field(:age, {"1": "A", "2": "B", "3": "C"}, selected: ["2", "3"]).should eq(expected)
160 | end
161 |
162 | it "creates a select_field with B and C selected (Int32 array)" do
163 | expected = "A B C "
164 | select_field(:age, {"1": "A", "2": "B", "3": "C"}, selected: [2, 3]).should eq(expected)
165 | end
166 |
167 | it "creates a select_field with B and C selected (Int32 | String array)" do
168 | expected = "A B C "
169 | select_field(:age, {"1": "A", "2": "B", "3": "C"}, selected: [2, "3"]).should eq(expected)
170 | end
171 |
172 | it "creates a select_field with single dimension array" do
173 | select_field(:age, ["A", "B"]).should eq("A B ")
174 | end
175 |
176 | it "creates a select_field with range" do
177 | select_field(:age, collection: 1..5).should eq("1 2 3 4 5 ")
178 | end
179 | end
180 |
181 | describe "#text_area" do
182 | it "creates a text_area" do
183 | text_area(:description, "My Great Textarea").should eq("")
184 | end
185 |
186 | it "allows for rows and cols to be specified" do
187 | text_area(:description, "My Great Textarea", cols: 5, rows: 10).should eq("")
188 | end
189 |
190 | it "allows for size to be specified" do
191 | text_area(:description, "My Great Textarea", size: "5x10").should eq("")
192 | end
193 | end
194 |
195 | describe "#submit" do
196 | it "creates a submit with no parameters" do
197 | submit.should eq(" ")
198 | end
199 |
200 | it "creates a submit with no parameters except id" do
201 | submit(id: :submit_button).should eq(" ")
202 | end
203 |
204 | it "creates a submit with value parameter" do
205 | submit(:create).should eq(" ")
206 | end
207 |
208 | it "creates a submit with value and id parameters" do
209 | submit(:create, id: "my-submit-tag").should eq(" ")
210 | end
211 | end
212 |
213 | describe "#check_box" do
214 | it "creates a check_box with yes/no" do
215 | expected = " "
216 | check_box(:allowed, checked_value: "yes", unchecked_value: "no").should eq(expected)
217 | end
218 |
219 | it "creates a check_box with only value" do
220 | expected = " "
221 | check_box(:allowed).should eq(expected)
222 | end
223 |
224 | it "marks box as checked" do
225 | expected = " "
226 | check_box(:allowed, checked: true).should eq(expected)
227 | end
228 |
229 | it "marks box as not checked" do
230 | expected = " "
231 | check_box(:allowed, checked: false).should eq(expected)
232 | end
233 | end
234 | end
235 |
--------------------------------------------------------------------------------
/spec/jasper_helpers/kit_spec.cr:
--------------------------------------------------------------------------------
1 | require "../spec_helper"
2 |
3 | describe JasperHelpers::Kit do
4 | describe "#css_safe" do
5 | it "creates safe id from bracketed name" do
6 | Kit.css_safe("test[name]").should eq("test_name")
7 | end
8 |
9 | it "shouldn't put more than underscores in a row or have ending or leading underscores" do
10 | Kit.css_safe(" david[bowie!]{rules}").should eq("david_bowie_rules")
11 | end
12 | end
13 |
14 | describe "merging and sanitizing" do
15 | it "it merges hash and namedtuple together in the right order" do
16 | expected = {:name => "kalidan[storm_blessed]", :age => 22, :id => "oath[bringer]", :selected => "syl"}
17 | Kit.merge({:name => "kalidan[storm_blessed]", :age => 22}, {id: "oath[bringer]", selected: "syl"}).should eq(expected)
18 | end
19 |
20 | it "it merges and sanitizes params" do
21 | expected = {:name => "dragon[reborn]", :age => 22, :class => "rand al thor", :selected => "sidar"}
22 | Kit.safe_hash({:name => "dragon[reborn]", :age => 22}, {class: "rand 'al thor", selected: "sidar"}).should eq(expected)
23 | end
24 |
25 | it "it sanitizes id and class" do
26 | expected = {:name => "kalidan[storm_blessed]", :age => 22, :id => "oath_bringer"}
27 | Kit.sanitize({:name => "kalidan[storm_blessed]", :age => 22, :id => "oath[bringer]"}).should eq(expected)
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/jasper_helpers/links_spec.cr:
--------------------------------------------------------------------------------
1 | require "../spec_helper"
2 |
3 | describe JasperHelpers::Links do
4 | describe "#link_to" do
5 | it "works with body and url provided" do
6 | link_to("Save", "/save").should eq("Save ")
7 | end
8 |
9 | it "works with a block" do
10 | result = link_to("/save") do
11 | "Badge "
12 | end
13 |
14 | result.should eq("Badge ")
15 | end
16 |
17 | it "works with a class" do
18 | link_to("Save", "/save", class: "my-link").should eq("Save ")
19 | end
20 | end
21 |
22 | describe "#button_to" do
23 | it "works with body and url provided" do
24 | button_to("Save", "/save").should eq expected_form
25 | end
26 |
27 | it "changes the method when provided" do
28 | button_to("Save", "/save", :put).should eq expected_form(override_method: :put)
29 | end
30 |
31 | context "with block" do
32 | it "renders form with hidden field" do
33 | form = button_to("Save", "/save", :delete) do
34 | hidden_field(:_csrf, value: "some value")
35 | end
36 |
37 | form.should eq expected_form(hidden_field(:_csrf, value: "some value"), :delete)
38 | end
39 |
40 | it "renders form with hidden field" do
41 | form = button_to("Save", "/save", :delete) do
42 | hidden_field(:_csrf, value: "some value") + %(Random text)
43 | end
44 |
45 | form.should eq expected_form(
46 | hidden_field(:_csrf, value: "some value") + %(Random text), :delete
47 | )
48 | end
49 |
50 | it "renders form with hidden field and styled button" do
51 | form = button_to("Save", "/save", :delete, type: "button", class: "btn btn-primary") do
52 | hidden_field(:_csrf, value: "some value") + %(Random text)
53 | end
54 |
55 | form.should eq expected_form(
56 | hidden_field(:_csrf, value: "some value") + %(Random text),
57 | :delete,
58 | "btn btn-primary",
59 | "button"
60 | )
61 | end
62 | end
63 | end
64 | end
65 |
66 | def expected_form(content = "", override_method = nil, css_class = nil, type = "submit")
67 | <<-FORM
68 |
71 | FORM
72 | end
73 |
--------------------------------------------------------------------------------
/spec/jasper_helpers/tags_spec.cr:
--------------------------------------------------------------------------------
1 | require "../spec_helper"
2 |
3 | describe JasperHelpers::Tags do
4 | describe "#input_field" do
5 | it "id value works" do
6 | input_field(type: :text, id: "my-great-input").should eq(" ")
7 | end
8 |
9 | it "id value inside of options hash" do
10 | input_field(type: :text, options: {:id => "my-great-input"}).should eq(" ")
11 | end
12 |
13 | it "name value works" do
14 | input_field(type: :text, name: "my-great-input").should eq(" ")
15 | end
16 |
17 | it "disabled true only has key, not value" do
18 | input_field(type: :text, disabled: true).should eq(" ")
19 | end
20 | end
21 |
22 | describe "#content" do
23 | it "accepts a content string" do
24 | content(element_name: :span, options: {:id => "bar"}, content: "Hello").should eq("Hello ")
25 | end
26 |
27 | it "accepts a block as input" do
28 | result = content(element_name: :div, options: {:id => "foo"}) do
29 | content(element_name: :span, options: {:id => "bar"}, content: "Hello")
30 | end
31 |
32 | result.should eq("Hello
")
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/spec/jasper_helpers/text_spec.cr:
--------------------------------------------------------------------------------
1 | require "../spec_helper"
2 |
3 | describe JasperHelpers::Text do
4 | describe "#render_markdown" do
5 | it "returns html from markdown" do
6 | expected = "Hello world \n"
7 | render_markdown("# Hello world").should eq(expected)
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/spec_helper.cr:
--------------------------------------------------------------------------------
1 | require "spec"
2 | require "../src/jasper_helpers"
3 |
4 | include JasperHelpers
5 |
--------------------------------------------------------------------------------
/src/jasper_helpers.cr:
--------------------------------------------------------------------------------
1 | require "./jasper_helpers/*"
2 |
3 | module JasperHelpers
4 | alias OptionHash = Hash(Symbol, Nil | String | Symbol | Bool | Int8 | Int16 | Int32 | Int64 | Float32 | Float64 | Time | Bytes | Array(String) | Array(Int32) | Array(String | Int32))
5 |
6 | include Tags
7 | include Text
8 | include Forms
9 | include Links
10 | end
11 |
--------------------------------------------------------------------------------
/src/jasper_helpers/forms.cr:
--------------------------------------------------------------------------------
1 | module JasperHelpers::Forms
2 | # text_field
3 | def text_field(name : String | Symbol, **options : Object)
4 | options_hash = Kit.safe_hash({:name => name, :id => name}, options)
5 | type = options[:type]? || :text
6 | input_field(type: type, options: options_hash.reject(:type))
7 | end
8 |
9 | def text_field(name : String | Symbol)
10 | text_field(name, id: name)
11 | end
12 |
13 | # file_field
14 | def file_field(name : String | Symbol, **options : Object)
15 | options_hash = Kit.safe_hash({:name => name, :id => name}, options)
16 | type = options[:type]? || :file
17 | input_field(type: type, options: options_hash.reject(:type))
18 | end
19 |
20 | def file_field(name : String | Symbol)
21 | file_field(name, id: name)
22 | end
23 |
24 | def label(name : String | Symbol, content : String? = nil, **options : Object)
25 | options_hash = Kit.safe_hash({for: name, id: "#{Kit.css_safe(name)}_label"}, options)
26 | content(element_name: :label, content: (content ? content : name.to_s.capitalize), options: options_hash)
27 | end
28 |
29 | def label(name : String | Symbol, content : String? = nil)
30 | label(name, content: (content ? content : name.to_s.capitalize), for: name, id: "#{Kit.css_safe(name)}_label")
31 | end
32 |
33 | def label(name : String | Symbol)
34 | content = "#{yield}"
35 | label(name, content: content, for: name, id: "#{Kit.css_safe(name)}_label")
36 | end
37 |
38 | def wrapper_field(*args)
39 | return "" unless args.first?
40 | args.join("")
41 | end
42 |
43 | # form
44 | def form(method = :post, **options : Object, &block)
45 | options_hash = Kit.safe_hash(options, {:method => (method == :get ? :get : :post)})
46 | options_hash[:enctype] = "multipart/form-data" if options_hash[:multipart]? == true
47 | content(element_name: :form, options: options_hash) do
48 | String.build do |str|
49 | str << hidden_field(name: "_method", value: method) unless [:get, :post].includes?(method)
50 | str << yield
51 | end
52 | end
53 | end
54 |
55 | def form(method = :post, &block)
56 | form(:post, class: "amber_form", &block)
57 | end
58 |
59 | # hidden_field
60 | def hidden_field(name : String | Symbol, **options : Object)
61 | options_hash = Kit.safe_hash({:name => name, :id => name}, options)
62 | input_field(type: :hidden, options: options_hash)
63 | end
64 |
65 | def hidden_field(name : String | Symbol)
66 | hidden_field(name: name, id: name)
67 | end
68 |
69 | # select_field
70 | # with collection Array(Array)
71 | def select_field(name : String | Symbol, collection : Array(Array), **options : Object)
72 | options_hash = Kit.safe_hash(options, {:name => name})
73 | selected = [options_hash.delete(:selected)].flatten.map(&.to_s)
74 | content(element_name: :select, options: options_hash) do
75 | String.build do |str|
76 | collection.map do |item|
77 | str << %(#{item[1]} )
78 | end
79 | end
80 | end
81 | end
82 |
83 | # Utilizes method above for when options are not defined and sets class and id.
84 | def select_field(name : String | Symbol, collection : Array(Array))
85 | select_field(name, collection, class: name, id: name)
86 | end
87 |
88 | # with collection Array(Hash)
89 | def select_field(name : String | Symbol, collection : Array(Hash), **options : Object)
90 | select_field(name, collection.map(&.first.to_a), **options)
91 | end
92 |
93 | def select_field(name : String | Symbol, collection : Array(Hash))
94 | select_field(name, collection.map(&.first.to_a), class: name, id: name)
95 | end
96 |
97 | # with collection Hash
98 | def select_field(name : String | Symbol, collection : Hash, **options : Object)
99 | select_field(name, collection.map { |k, v| [k, v] }, **options)
100 | end
101 |
102 | def select_field(name : String | Symbol, collection : Hash)
103 | select_field(name, collection.map { |k, v| [k, v] }, class: name, id: name)
104 | end
105 |
106 | def select_field(name : String | Symbol, collection : NamedTuple, **options : Object)
107 | select_field(name, collection.map { |k, v| [k, v] }, **options)
108 | end
109 |
110 | def select_field(name : String | Symbol, collection : NamedTuple)
111 | select_field(name, collection.map { |k, v| [k, v] }, class: name, id: name)
112 | end
113 |
114 | # with collection Array
115 | def select_field(name : String | Symbol, collection : Array | Range, **options : Object)
116 | select_field(name, collection.map { |i| [i.to_s, i.to_s.capitalize] }, **options)
117 | end
118 |
119 | def select_field(name : String | Symbol, collection : Array | Range)
120 | select_field(name, collection.map { |i| [i.to_s, i.to_s.capitalize] }, class: name, id: name)
121 | end
122 |
123 | # text_area
124 | def text_area(name : String | Symbol, content : String?, **options : Object)
125 | options_hash = Kit.safe_hash({:name => name, :id => name}, options)
126 | options_hash[:cols], options_hash[:rows] = options_hash.delete(:size).to_s.split("x") if options.has_key?(:size)
127 | content(element_name: :textarea, options: options_hash) do
128 | content
129 | end
130 | end
131 |
132 | def text_area(name : String | Symbol, content : String?)
133 | text_area(name, content, id: name)
134 | end
135 |
136 | # submit
137 | def submit(value : String | Symbol = "Save Changes", **options : Object)
138 | options_hash = Kit.safe_hash({value: value}, options)
139 | input_field(type: :submit, options: options_hash)
140 | end
141 |
142 | def submit(value : String | Symbol = "Save Changes")
143 | submit(value: value.to_s, id: value.to_s.gsub(" ", "_").downcase)
144 | end
145 |
146 | # check_box
147 | def check_box(name : String | Symbol, checked_value = "1", unchecked_value = "0", **options : Object)
148 | options_hash = Kit.safe_hash({:name => name, :id => name, :value => checked_value}, options)
149 | # Allows you to pass in checked=true/false
150 | if options_hash[:checked]?
151 | options_hash[:checked] = "checked"
152 | else
153 | options_hash.delete(:checked)
154 | end
155 |
156 | String.build do |str|
157 | str << input_field(type: :checkbox, options: options_hash)
158 | str << hidden_field(name, value: unchecked_value, id: "#{options_hash[:id]}_default")
159 | end
160 | end
161 |
162 | def check_box(name : String | Symbol, checked_value = "1", unchecked_value = "0")
163 | check_box(name, checked_value: checked_value, unchecked_value: unchecked_value, id: name)
164 | end
165 | end
166 |
--------------------------------------------------------------------------------
/src/jasper_helpers/kit.cr:
--------------------------------------------------------------------------------
1 | module JasperHelpers::Kit
2 | extend self
3 |
4 | def css_safe(value)
5 | values = value.to_s.strip.split(' ')
6 | values.map { |v| v.gsub(/[^\w-]+/, " ").strip.gsub(/\s+/, "_") }.join(' ')
7 | end
8 |
9 | def merge(*options)
10 | options.reduce(OptionHash.new) do |h, opts|
11 | h.merge!(opts.to_h)
12 | end
13 | end
14 |
15 | def sanitize(options : OptionHash)
16 | name = options[:name]?.to_s
17 | [:id, :class].each do |k|
18 | next if (v = options[k]?.to_s).empty?
19 | v = v.gsub(" ", "_").downcase if v == name
20 | options[k] = css_safe(v)
21 | end
22 | options
23 | end
24 |
25 | def safe_hash(*options)
26 | sanitize(merge(*options))
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/src/jasper_helpers/links.cr:
--------------------------------------------------------------------------------
1 | module JasperHelpers::Links
2 |
3 | # Creates an anchor,` `, tag rendering the `body` parameter as content. The anchor tag's `href` will be set to the `url` parameter provided. The `options` parameter is be used to provide additional configurations or attributes for the tag.
4 | #
5 | # Example:
6 | # ```
7 | # link_to("Go to GitHub", "http://github.com", { id: "github-url" })
8 | # ```
9 | #
10 | # Produces:
11 | # ```
12 | # Go to GitHub
13 | # ```
14 | def link_to(body : String, url : String, **options : Object)
15 | content(element_name: :a, content: body, options: {:href => url}.merge(options.to_h))
16 | end
17 |
18 | # :ditto:
19 | #
20 | # With the exception of not accepting tag options
21 | def link_to(body : String, url : String)
22 | content(element_name: :a, content: body, options: {:href => url})
23 | end
24 |
25 | # Creates an anchor,` `, tag with the `href` attribute set to the `url` parameter provided.
26 | # The `options` parameter can be used to provide additional configurations or attributes for the tag.
27 | #
28 | # Example:
29 | # ```
30 | # link_to("http://github.com", { id: "github-url" })
31 | # ```
32 | #
33 | # Produces:
34 | # ```
35 | #
36 | # ```
37 | def link_to(url : String, **options : Object, &block)
38 | link_to(yield, url, **options)
39 | end
40 |
41 | # :ditto:
42 | #
43 | # With the exception of not accepting tag options
44 | def link_to(url : String, &block)
45 | link_to(yield, url)
46 | end
47 |
48 | def button_to(body : String, url : String, method : Symbol = :post)
49 | form(action: url, method: method) do
50 | content(element_name: :button, content: body, options: {:type => "submit"})
51 | end
52 | end
53 |
54 | def button_to(body : String, url : String, method : Symbol = :post)
55 | form(action: url, method: method) do
56 | String.build do |str|
57 | str << yield
58 | str << content(element_name: :button, content: body, options: {:type => "submit"})
59 | end
60 | end
61 | end
62 |
63 | def button_to(body : String, url : String, method : Symbol = :post, **options : Object)
64 | form(action: url, method: method) do
65 | content(element_name: :button, content: body, options: options.to_h)
66 | end
67 | end
68 |
69 | def button_to(body : String, url : String, method : Symbol = :post, **options : Object, &block)
70 | form(action: url, method: method) do
71 | String.build do |str|
72 | str << yield
73 | str << content(element_name: :button, content: body, options: options.to_h)
74 | end
75 | end
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/src/jasper_helpers/tags.cr:
--------------------------------------------------------------------------------
1 | module JasperHelpers::Tags
2 | INPUT_BOOLEAN_ATTRIBUTES = [:disabled]
3 |
4 | def input_field(type : Symbol, **options)
5 | options = options.to_h
6 | input_field_string(type: type, options: options)
7 | end
8 |
9 | def input_field(type : Symbol, options : OptionHash)
10 | input_field_string(type: type, options: options)
11 | end
12 |
13 | def input_field_string(type : Symbol, options : OptionHash)
14 | tag_options = prepare_input_field_options(options: options)
15 | " "
16 | end
17 |
18 | # helper for input field options
19 | def prepare_input_field_options(options : OptionHash)
20 | options[:id] = options[:name] if (options[:name]?) && !(options[:id]?)
21 | boolean_options = options.select(INPUT_BOOLEAN_ATTRIBUTES)
22 | tag_options = options.reject!(INPUT_BOOLEAN_ATTRIBUTES).map { |k, v| "#{k}=\"#{v}\"" }
23 | tag_options = tag_options << boolean_options.keys.join(" ") if !boolean_options.empty?
24 | tag_options = tag_options.join(" ")
25 | tag_options
26 | end
27 |
28 | def content(element_name : Symbol, content : String, options : OptionHash)
29 | content(element_name: element_name, options: options) do
30 | content
31 | end
32 | end
33 |
34 | # Builds an arbitrary HTML tag for the provided `element_name` using `options` to configure additional parameters for accordingly.
35 | #
36 | # Example
37 | # ```
38 | # content(:a, { aria_data_toggle: true, href: "http://example.com" })
39 | # ```
40 | #
41 | # Produces:
42 | # ```text
43 | #
44 | # ```
45 | def content(element_name : Symbol, options : OptionHash, &block)
46 | String.build do |str|
47 | str << "<#{element_name}"
48 | options.each do |k, v|
49 | next if v.nil?
50 | str << %( #{k}="#{v}")
51 | end
52 | str << ">#{yield}#{element_name}>"
53 | end
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/src/jasper_helpers/text.cr:
--------------------------------------------------------------------------------
1 | require "markd"
2 |
3 | module JasperHelpers::Text
4 | def render_markdown(markdown : String)
5 | Markd.to_html(markdown)
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/src/jasper_helpers/version.cr:
--------------------------------------------------------------------------------
1 | module JasperHelpers
2 | VERSION = "1.2.2"
3 | end
4 |
--------------------------------------------------------------------------------