UNPKG

19.5 kBMarkdownView Raw
1# commonform-html
2
3render [Common Forms](https://www.npmjs.com/package/commonform-validate) in HTML
4
5```javascript
6var html = require('commonform-html')
7```
8
9Call the exported function with a Common Form, receive a string of HTML:
10
11```javascript
12var assert = require('assert')
13
14assert.deepStrictEqual(
15 html({ content: ['Just a test'] }),
16 '<div class="article"><p>Just a test</p></div>'
17)
18
19assert.deepStrictEqual(
20 html({
21 content: [
22 {
23 heading: 'A',
24 form: { content: ['This is A'] }
25 },
26 {
27 heading: 'B',
28 form: { content: ['This is B'] }
29 }
30 ]
31 }),
32 [
33 '<div class="article">',
34 '<div class="section">',
35 '<h1>A</h1>',
36 '<p>This is A</p>',
37 '</div>',
38 '<div class="section">',
39 '<h1>B</h1>',
40 '<p>This is B</p>',
41 '</div>',
42 '</div>'
43 ]
44 .join('')
45)
46```
47
48You can also pass an `Array` of fill-in-the-blank values:
49
50```javascript
51assert.deepStrictEqual(
52 html(
53 {
54 content: [
55 { blank: '' },
56 { form: { content: ['Another ', { blank: '' }] } }
57 ]
58 },
59 [
60 { blank: ['content', 0], value: 'Joe' },
61 { blank: ['content', 1, 'form', 'content', 1], value: 'Bob' }
62 ]
63 ),
64 (
65 '<div class="article">' +
66 '<p><span class="blank">Joe</span></p>' +
67 '<div class="section">' +
68 '<p>Another <span class="blank">Bob</span></p>' +
69 '</div>' +
70 '</div>'
71 )
72)
73```
74
75A final argument of `{ html5: true }` specifies HTML5 output:
76
77```javascript
78assert.deepStrictEqual(
79 html({ content: ['Just a test'] }, [], { html5: true }),
80 '<article><p>Just a test</p></article>'
81)
82
83assert.deepStrictEqual(
84 html(
85 {
86 content: [
87 'First text defines a ',
88 { definition: 'Term' },
89 {
90 heading: 'A',
91 form: { content: ['This is A'] }
92 },
93 'Middle text uses a ',
94 { use: 'Term' },
95 {
96 heading: 'B',
97 form: { content: ['This is B'] }
98 },
99 'Last text references ',
100 { reference: 'Elsewhere' }
101 ]
102 },
103 [],
104 { html5: true }
105 ),
106 [
107 '<article>', // not <div class="article">
108 '<p>',
109 'First text defines a ',
110 '<dfn>Term</dfn>', // not <span class="definition">
111 '</p>',
112 '<section>', // not <div class="section">
113 '<h1>A</h1>',
114 '<p>This is A</p>',
115 '</section>',
116 '<p>',
117 'Middle text uses a ',
118 '<span class="term">Term</span>',
119 '</p>',
120 '<section>',
121 '<h1>B</h1>',
122 '<p>This is B</p>',
123 '</section>',
124 '<p>',
125 'Last text references ',
126 '<span class="reference">Elsewhere</span>',
127 '</p>',
128 '</article>'
129 ]
130 .join('')
131)
132```
133
134The option `{ lists: true }` renders any series of forms without headings as an ordered list:
135
136```javascript
137assert.deepStrictEqual(
138 html(
139 {
140 content: [
141 {
142 heading: 'First-Level Heading',
143 form: {
144 content: [
145 'Here comes a list.',
146 { form: { content: ['Apple'] } },
147 { form: { content: ['Orange'] } },
148 'And another one!',
149 {
150 form: {
151 content: [
152 'Red',
153 { form: { content: ['Red-Orange'] } },
154 { form: { content: ['Red-Yellow'] } }
155 ]
156 }
157 },
158 { form: { content: ['Purple'] } },
159 {
160 form: {
161 content: [
162 { form: { content: ['More'] } },
163 { form: { content: ['Even More'] } }
164 ]
165 }
166 },
167 'Last text!'
168 ]
169 }
170 }
171 ]
172 },
173 [],
174 { html5: true, lists: true }
175 ),
176 [
177 '<article>',
178 '<section>',
179 '<h1>First-Level Heading</h1>',
180 '<p>Here comes a list.</p>',
181 '<ol>',
182 '<li><p>Apple</p></li>',
183 '<li><p>Orange</p></li>',
184 '</ol>',
185 '<p>And another one!</p>',
186 '<ol>',
187 '<li>',
188 '<p>Red</p>',
189 '<ol>',
190 '<li><p>Red-Orange</p></li>',
191 '<li><p>Red-Yellow</p></li>',
192 '</ol>',
193 '</li>',
194 '<li><p>Purple</p></li>',
195 '<li>',
196 '<ol>',
197 '<li><p>More</p></li>',
198 '<li><p>Even More</p></li>',
199 '</ol>',
200 '</li>',
201 '</ol>',
202 '<p>Last text!</p>',
203 '</section>',
204 '</article>'
205 ]
206 .join('')
207)
208```
209
210The option `{ ids: true }` renders headings and references with IDs:
211
212```javascript
213assert.deepStrictEqual(
214 html(
215 {
216 content: [
217 {
218 heading: 'First Heading',
219 form: {
220 content: [
221 'first heading content'
222 ]
223 }
224 },
225 {
226 heading: 'Second Heading',
227 form: {
228 content: [
229 'reference to ',
230 { reference: 'First Heading' }
231 ]
232 }
233 }
234 ]
235 },
236 [],
237 { html5: true, ids: true }
238 ),
239 [
240 '<article>',
241 '<section>',
242 '<h1 id="first-heading">First Heading</h1>',
243 '<p>first heading content</p>',
244 '</section>',
245 '<section>',
246 '<h1 id="second-heading">Second Heading</h1>',
247 '<p>reference to <a class="reference" href="#first-heading">First Heading</a></p>',
248 '</section>',
249 '</article>'
250 ]
251 .join('')
252)
253```
254
255You can also set a title, version, or both:
256
257```javascript
258assert.deepStrictEqual(
259 html(
260 { content: ['Hello, ', { blank: '' }] },
261 [{ blank: ['content', 1], value: 'Joe' }],
262 { title: 'Welcome' }
263 ),
264 [
265 '<div class="article">',
266 '<h1>Welcome</h1>',
267 '<p>',
268 'Hello, <span class="blank">Joe</span>',
269 '</p>',
270 '</div>'
271 ]
272 .join('')
273)
274
275assert.deepStrictEqual(
276 html(
277 { content: ['Hello, ', { blank: '' }] },
278 [{ blank: ['content', 1], value: 'Joe' }],
279 { title: 'Welcome', version: '1.0.0' }
280 ),
281 [
282 '<div class="article">',
283 '<h1>Welcome</h1>',
284 '<p class="version">1.0.0</p>',
285 '<p>',
286 'Hello, <span class="blank">Joe</span>',
287 '</p>',
288 '</div>'
289 ]
290 .join('')
291)
292```
293
294Set `options.hash` to print the form hash at the top:
295
296```javascript
297assert.deepStrictEqual(
298 html(
299 { content: ['Hello, ', { blank: '' }] },
300 [{ blank: ['content', 1], value: 'Joe' }],
301 { title: 'Welcome', version: '1.0.0', hash: true }
302 ),
303 [
304 '<div class="article">',
305 '<h1>Welcome</h1>',
306 '<p class="version">1.0.0</p>',
307 '<p class="hash"><code>' +
308 'd36c54da27de611b3a9ce7d08638bbd2' +
309 '00cf5f3bb41d59320d04bba02ca48f85' +
310 '</code></p>',
311 '<p>',
312 'Hello, <span class="blank">Joe</span>',
313 '</p>',
314 '</div>'
315 ]
316 .join('')
317)
318```
319
320The option `{ classNames: ["test"] }` adds custom class names to the root element.
321
322```javascript
323assert.deepStrictEqual(
324 html(
325 { content: ['Hello, Joe.'] },
326 [],
327 { classNames: ['test'] }
328 ),
329 [
330 '<div class="article test">',
331 '<p>',
332 'Hello, Joe.',
333 '</p>',
334 '</div>'
335 ]
336 .join('')
337)
338
339assert.deepStrictEqual(
340 html(
341 { content: ['Hello, Joe.'] },
342 [],
343 { html5: true, classNames: ['test'] }
344 ),
345 [
346 '<article class="test">',
347 '<p>',
348 'Hello, Joe.',
349 '</p>',
350 '</article>'
351 ]
352 .join('')
353)
354```
355
356The option `{ depth: n }` demotes all headings by `n` levels. For example, `{ depth: 1 }` demotes `<h1>`s to `<h2>`s, and so on.
357
358```javascript
359assert.deepStrictEqual(
360 html(
361 {
362 content: [
363 {
364 heading: 'A',
365 form: { content: ['This is A'] }
366 },
367 {
368 heading: 'B',
369 form: { content: ['This is B'] }
370 }
371 ]
372 },
373 [],
374 { depth: 2 }
375 ),
376 [
377 '<div class="article">',
378 '<div class="section">',
379 '<h3>A</h3>',
380 '<p>This is A</p>',
381 '</div>',
382 '<div class="section">',
383 '<h3>B</h3>',
384 '<p>This is B</p>',
385 '</div>',
386 '</div>'
387 ]
388 .join('')
389)
390```
391
392Set `options.smartify` to replace ASCII punctuation with Unicode punctuation:
393
394```javascript
395assert.deepStrictEqual(
396 html(
397 { content: [{ use: 'Purchaser' }, "'s address is ", { blank: '' }] },
398 // ^ straight
399 [],
400 { smartify: true }
401 ),
402 [
403 '<div class="article">',
404 '<p>',
405 '<span class="term">Purchaser</span>’s ',
406 // ^ curly
407 'address is ',
408 '<span class="blank">[•]</span>',
409 '</p>',
410 '</div>'
411 ]
412 .join('')
413)
414```
415
416The option `{ annotations: [] }` renders annotations in context.
417
418```javascript
419assert.deepStrictEqual(
420 html(
421 {
422 content: [
423 {
424 heading: 'A',
425 form: { content: ['This is A'] }
426 },
427 {
428 heading: 'B',
429 form: { content: ['This is B'] }
430 },
431 {
432 heading: 'C',
433 form: {
434 content: [
435 { form: { content: ['1'] } },
436 { form: { content: ['2'] } },
437 { form: { content: ['3'] } }
438 ]
439 }
440 }
441 ]
442 },
443 [],
444 {
445 lists: true,
446 annotations: [
447 {
448 path: ['content', 0, 'form', 'content', 0],
449 level: 'info',
450 message: 'Annotation to A.'
451 },
452 {
453 path: ['content', 1, 'form', 'content', 0],
454 level: 'error',
455 message: 'Annotation to B.'
456 },
457 {
458 path: ['content', 2, 'form', 'content', 1, 'form', 'content', 0],
459 level: 'error',
460 message: 'Annotation to 2.'
461 }
462 ]
463 }
464 ),
465 [
466 '<div class="article">',
467 '<div class="section">',
468 '<h1>A</h1>',
469 '<div class="annotation info"><p>Annotation to A.</p></div>',
470 '<p>This is A</p>',
471 '</div>',
472 '<div class="section">',
473 '<h1>B</h1>',
474 '<div class="annotation error"><p>Annotation to B.</p></div>',
475 '<p>This is B</p>',
476 '</div>',
477 '<div class="section">',
478 '<h1>C</h1>',
479 '<ol>',
480 '<li><p>1</p></li>',
481 '<li>',
482 '<div class="annotation error"><p>Annotation to 2.</p></div>',
483 '<p>2</p>',
484 '</li>',
485 '<li><p>3</p></li>',
486 '</ol>',
487 '</div>',
488 '</div>'
489 ]
490 .join('')
491)
492```
493
494With `{ html5: true }`, annotations render as `<aside>`s.
495
496```javascript
497assert.deepStrictEqual(
498 html(
499 {
500 content: [
501 {
502 heading: 'A',
503 form: { content: ['This is A'] }
504 },
505 {
506 heading: 'B',
507 form: { content: ['This is B'] }
508 },
509 {
510 heading: 'C',
511 form: {
512 content: [
513 { form: { content: ['1'] } },
514 { form: { content: ['2'] } },
515 { form: { content: ['3'] } }
516 ]
517 }
518 }
519 ]
520 },
521 [],
522 {
523 html5: true,
524 lists: true,
525 annotations: [
526 {
527 path: ['content', 0, 'form', 'content', 0],
528 level: 'info',
529 message: 'Annotation to A.'
530 },
531 {
532 path: ['content', 1, 'form', 'content', 0],
533 level: 'error',
534 message: 'Annotation to B.'
535 },
536 {
537 path: ['content', 2, 'form', 'content', 1, 'form', 'content', 0],
538 level: 'error',
539 message: 'Annotation to 2.'
540 }
541 ]
542 }
543 ),
544 [
545 '<article>',
546 '<section>',
547 '<h1>A</h1>',
548 '<aside class="annotation info"><p>Annotation to A.</p></aside>',
549 '<p>This is A</p>',
550 '</section>',
551 '<section>',
552 '<h1>B</h1>',
553 '<aside class="annotation error"><p>Annotation to B.</p></aside>',
554 '<p>This is B</p>',
555 '</section>',
556 '<section>',
557 '<h1>C</h1>',
558 '<ol>',
559 '<li><p>1</p></li>',
560 '<li>',
561 '<aside class="annotation error"><p>Annotation to 2.</p></aside>',
562 '<p>2</p>',
563 '</li>',
564 '<li><p>3</p></li>',
565 '</ol>',
566 '</section>',
567 '</article>'
568 ]
569 .join('')
570)
571```
572
573Annotations to the root of the form appear just within the root element:
574
575```javascript
576var lint = require('commonform-lint')
577var form = { content: ['See ', { reference: 'Nonexistent' }] }
578var annotations = lint(form)
579assert.deepStrictEqual(
580 html(form, [], {
581 html5: true,
582 lists: true,
583 annotations: annotations
584 }),
585 [
586 '<article>',
587 '<aside class="annotation error">',
588 '<p>The heading &quot;Nonexistent&quot; is referenced, but not used.</p>',
589 '</aside>',
590 '<p>See <span class="reference">Nonexistent</span></p>',
591 '</article>'
592 ]
593 .join('')
594)
595```
596
597Supports components:
598
599```javascript
600assert.deepStrictEqual(
601 html(
602 {
603 content: [
604 {
605 heading: 'License Grant',
606 component: 'https://commonform.org/kemitchell/apache-style-license-grant',
607 version: '1.0.0',
608 substitutions: {
609 terms: {
610 Licensor: 'Vendor',
611 Licensee: 'Customer',
612 Program: 'Software'
613 },
614 headings: {
615 'Express Warranties': 'Guarantees'
616 },
617 blanks: {
618 1: 'United States'
619 }
620 }
621 }
622 ]
623 },
624 [],
625 {
626 html5: true,
627 lists: true
628 }
629 ),
630 [
631 '<article>',
632 '<section class="component">',
633 '<h1>License Grant</h1>',
634 '<p>',
635 'Incorporate ',
636 '<a href="https://commonform.org/kemitchell/apache-style-license-grant/1.0.0">',
637 'https://commonform.org/kemitchell/apache-style-license-grant/1.0.0',
638 '</a>',
639 ' substituting:',
640 '</p>',
641 '<ul>',
642 '<li>the term "Customer" for the term "Licensee"</li>',
643 '<li>the term "Vendor" for the term "Licensor"</li>',
644 '<li>the term "Software" for the term "Program"</li>',
645 '<li>references to "Guarantees" for references to "Express Warranties"</li>',
646 '<li>"United States" for blank 1</li>',
647 '</ul>',
648 '</section>',
649 '</article>'
650 ]
651 .join('')
652)
653
654assert.deepStrictEqual(
655 html(
656 {
657 content: [
658 {
659 component: 'https://commonform.org/kemitchell/apache-style-license-grant',
660 version: '1.0.0',
661 substitutions: {
662 terms: {
663 Licensor: 'Vendor',
664 Licensee: 'Customer',
665 Program: 'Software'
666 },
667 headings: {
668 'Express Warranties': 'Guarantees'
669 },
670 blanks: {
671 1: 'United States'
672 }
673 }
674 }
675 ]
676 },
677 [],
678 {
679 html5: true,
680 lists: true
681 }
682 ),
683 [
684 '<article>',
685 '<ol>',
686 '<li class="component">',
687 '<p>',
688 'Incorporate ',
689 '<a href="https://commonform.org/kemitchell/apache-style-license-grant/1.0.0">',
690 'https://commonform.org/kemitchell/apache-style-license-grant/1.0.0',
691 '</a>',
692 ' substituting:',
693 '</p>',
694 '<ul>',
695 '<li>the term "Customer" for the term "Licensee"</li>',
696 '<li>the term "Vendor" for the term "Licensor"</li>',
697 '<li>the term "Software" for the term "Program"</li>',
698 '<li>references to "Guarantees" for references to "Express Warranties"</li>',
699 '<li>"United States" for blank 1</li>',
700 '</ul>',
701 '</li>',
702 '</ol>',
703 '</article>'
704 ]
705 .join('')
706)
707```
708
709If you pass a form with resolved components labeled with appropriate metadata, you can specify one of three styles for rendering them specially:
710
711```javascript
712var formWithLoaded = {
713 content: [
714 {
715 form: {
716 content: [
717 'Except under ', { reference: 'Warranties' },
718 ', the ', { use: 'Vendor' },
719 ' disclaimers all warranties to the ', { use: 'Customer' },
720 ' related to the ', { use: 'Software' }, '.'
721 ]
722 },
723 reference: {
724 component: 'https://example.com/toy-disclaimer',
725 version: '1.0.0',
726 substitutions:{
727 terms: {
728 Seller: 'Vendor',
729 Buyer: 'Customer',
730 Product: 'Software'
731 },
732 headings: {
733 Warranties: 'Quality Assurances'
734 },
735 blanks: {}
736 }
737 },
738 component: {
739 publisher: 'Example Publisher',
740 name: 'Toy Disclaimer',
741 version: '1.0.0'
742 }
743 }
744 ]
745}
746
747assert.deepStrictEqual(
748 html(formWithLoaded, [], {
749 html5: true,
750 lists: true,
751 loadedComponentStyle: 'both' // reference and copy
752 }),
753 [
754 '<article>',
755 '<ol>',
756 '<li class="component">',
757 '<p>',
758 'Incorporate ',
759 '<a href="https://example.com/toy-disclaimer/1.0.0">Example Publisher Toy Disclaimer 1.0.0</a>',
760 ' substituting:',
761 '</p>',
762 '<ul>',
763 '<li>the term "Customer" for the term "Buyer"</li>',
764 '<li>the term "Software" for the term "Product"</li>',
765 '<li>the term "Vendor" for the term "Seller"</li>',
766 '<li>references to "Quality Assurances" for references to "Warranties"</li>',
767 '</ul>',
768 '<p>Quoting for convenience, with any conflicts resolved in favor of the standard:</p>',
769 '<blockquote>',
770 '<p>',
771 'Except under <span class="reference">Warranties</span>, ',
772 'the <span class="term">Vendor</span> ',
773 'disclaimers all warranties to the <span class="term">Customer</span> ',
774 'related to the <span class="term">Software</span>.',
775 '</p>',
776 '</blockquote>',
777 '</li>',
778 '</ol>',
779 '</article>'
780 ]
781 .join('')
782)
783
784assert.deepStrictEqual(
785 html(formWithLoaded, [], {
786 html5: true,
787 lists: true,
788 loadedComponentStyle: 'both',
789 quoteComponentText: 'For reference:' // custom text between
790 }),
791 [
792 '<article>',
793 '<ol>',
794 '<li class="component">',
795 '<p>',
796 'Incorporate ',
797 '<a href="https://example.com/toy-disclaimer/1.0.0">Example Publisher Toy Disclaimer 1.0.0</a>',
798 ' substituting:',
799 '</p>',
800 '<ul>',
801 '<li>the term "Customer" for the term "Buyer"</li>',
802 '<li>the term "Software" for the term "Product"</li>',
803 '<li>the term "Vendor" for the term "Seller"</li>',
804 '<li>references to "Quality Assurances" for references to "Warranties"</li>',
805 '</ul>',
806 '<p>',
807 'For reference:', // here
808 '</p>',
809 '<blockquote>',
810 '<p>',
811 'Except under <span class="reference">Warranties</span>, ',
812 'the <span class="term">Vendor</span> ',
813 'disclaimers all warranties to the <span class="term">Customer</span> ',
814 'related to the <span class="term">Software</span>.',
815 '</p>',
816 '</blockquote>',
817 '</li>',
818 '</ol>',
819 '</article>'
820 ]
821 .join('')
822)
823assert.deepStrictEqual(
824 html(formWithLoaded, [], {
825 html5: true,
826 lists: true,
827 incorporateComponentText: 'Include',
828 loadedComponentStyle: 'reference' // just reference
829 }),
830 [
831 '<article>',
832 '<ol>',
833 '<li class="component">',
834 '<p>',
835 'Include ',
836 '<a href="https://example.com/toy-disclaimer/1.0.0">Example Publisher Toy Disclaimer 1.0.0</a>',
837 ' substituting:',
838 '</p>',
839 '<ul>',
840 '<li>the term "Customer" for the term "Buyer"</li>',
841 '<li>the term "Software" for the term "Product"</li>',
842 '<li>the term "Vendor" for the term "Seller"</li>',
843 '<li>references to "Quality Assurances" for references to "Warranties"</li>',
844 '</ul>',
845 '</li>',
846 '</ol>',
847 '</article>'
848 ]
849 .join('')
850)
851
852assert.deepStrictEqual(
853 html(formWithLoaded, [], {
854 html5: true,
855 lists: true,
856 loadedComponentStyle: 'copy'
857 }),
858 [
859 '<article>',
860 '<ol>',
861 '<li class="component">',
862 '<p>',
863 'Except under <span class="reference">Warranties</span>, ',
864 'the <span class="term">Vendor</span> ',
865 'disclaimers all warranties to the <span class="term">Customer</span> ',
866 'related to the <span class="term">Software</span>.',
867 '</p>',
868 '</li>',
869 '</ol>',
870 '</article>'
871 ]
872 .join('')
873)
874```