stanhope/
write_html.rs

1//! *Write a single HTML file from a single EBML file input*
2//! 
3//! Some assumptions are made:
4//! * Common HTML assets (CSS, template images) are in a folder ../assets/
5//! * Cross-crate knowledge of the Process struct is required
6//! 
7//! A single public function performs this HTML writing task, which calls private functions to write their portions of the HTML file.
8use crate::read_ebml::Process;
9use crate::read_ebml::Section;
10use crate::read_ebml::SubStep;
11use crate::read_ebml::Action;
12use crate::read_ebml::Requirement;
13use crate::read_ebml::Table;
14
15use std::fs::File;
16use std::fs::OpenOptions;
17use std::io::Write;
18
19/// Single public function that writes out a specially-formatted HTML file "filename" according to the data stored in the Process struct "p"
20/// 
21/// The HTML heavily depends on stanhope.css in the ../assets/ folder to look nice on screen and in print.
22/// Other template CSS files also go in ../assets/
23pub fn generate_complete_html(filename:&String, p:&Process) {
24
25    // Open the filename input to write HTML out to that file
26    let mut html_file = OpenOptions::new()
27        .read(true)
28        .write(true)
29        .create(true)
30        .open(filename)
31        .expect("cannot open file");
32
33    // Compose the first line of HTML file through just before the <head> element
34    compose_top_of_html_file(&mut html_file);
35
36    // Compose the <head> portion of the HTML
37    compose_html_head(&mut html_file, &p);
38
39    // Compose the <bod> portion of the HTML, which is the majority of the page
40    compose_html_body(&mut html_file,&p,p.get_all_sections());
41
42    // compose everything after the close of </body> (which is not much)
43    compose_bottom_of_html_file(&mut html_file);
44}
45
46/// Basic doctype declaration and *html* opening
47fn compose_top_of_html_file(f:&mut File) {
48    f.write(b"<!DOCTYPE html>\n").expect("write failed");
49    f.write(b"<html lang=\"en-US\">\n\n").expect("write failed");
50}
51
52/// Close *html*
53fn compose_bottom_of_html_file(f:&mut File) {
54    f.write(b"</html>").expect("write failed");
55}
56
57/// Head portion, which references external style files, stored in the Process struct at "Templates"
58fn compose_html_head(f:&mut File,p:&Process) {
59    f.write(b"\t<head>\n").expect("write failed");
60
61    let temp_str = String::from("\t\t<title>") + p.get_number() + " \"" + p.get_title() + "\"</title>\n";
62    f.write(temp_str.as_bytes()).expect("write failed");
63    f.write(b"\t\t<meta charset=\"utf-8\" />\n").expect("write failed");
64    let temp_str = String::from("\t\t<meta name = \"keywords\" content = \"") + p.get_subject() + ", " + p.get_product() + "\" />\n";
65    f.write(temp_str.as_bytes()).expect("write failed");
66    let temp_str = String::from("\t\t<meta name = \"description\" content = \"") + p.get_number() + " " + p.get_title() + "\" />\n";
67    f.write(temp_str.as_bytes()).expect("write failed");
68    let temp_str = String::from("\t\t<meta name = \"author\" content = \"") + p.get_author() + "\" />\n";
69    f.write(temp_str.as_bytes()).expect("write failed");
70    f.write(b"\t\t<meta http-equiv = \"Content-Type\" content = \"text/html; charset = UTF-8\" />\n").expect("write failed");
71    f.write(b"\t\t<link rel=\"shortcut icon\" sizes=\"16x16 32x32 48x48\" type=\"image/png\" href=\"../assets/favicon.png\" />\n").expect("write failed");
72    f.write(b"\t\t<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"../assets/favicon.png\" />\n").expect("write failed");
73    f.write(b"\t\t<link rel=\"mask-icon\" href=\"../assets/favicon.svg\" color=\"#000000\" />\n\n").expect("write failed");
74    
75    f.write(b"\t\t<!-- The stanhope.css applies to every process, regardless of program -->\n").expect("write failed");
76    f.write(b"\t\t<link rel=\"stylesheet\" href=\"../assets/stanhope.css\"/>\n").expect("write failed");
77    // Place to add reference to user-defined "Template" CSS files
78    // loop over vector
79    for template in p.get_all_templates() {
80        let temp_str = String::from("\t\t<link rel=\"stylesheet\" href=\"../assets/") + &template + "\"/>\n";
81        f.write(temp_str.as_bytes()).expect("write failed");
82    }
83    f.write(b"\t</head>\n\n").expect("write failed");
84}
85
86/// Body portion, which is the bulk of the HTML file. The composition is broken up into other sequential functions:
87/// * Title page (styled with pagebreak in stanhope.css)
88/// * Revision and Requirements page (also has pagebreak)
89/// * Table of Contents, which also includes Resources (also has pagebreak)
90/// * TODO: add placeholder sections for other user-defined templates, like boilerplate warnings for ESD if ESD is checked
91/// * All Process Sections
92/// * * For each sequential Section, write out each sequential Step
93/// * * For each sequential Step, write out each sequential SubStep
94/// * * Include special cases for Resource, etc.
95/// * Close out the sections and insert the banners at the top and bottom of the page
96fn compose_html_body(f:&mut File,p:&Process,s:&Vec<Section>) {
97    f.write(b"\t<body>\n\n").expect("write failed");
98    f.write(b"\t\t<div id=\"classification-header\" class=\"classification\"><p></p></div>\n").expect("write failed");
99
100    // I CAN'T KNOW HOW TO HEAR ANY MORE ABOUT TABLES
101    compose_html_body_title_page(f,p);
102    compose_html_body_rev_req_page(f,p);
103    compose_html_body_toc(f,p,s);
104
105    // Where most of the action is
106    compose_html_body_section(f,p,s);
107    compose_html_body_end_of_sections_and_banners(f,p);
108
109    f.write(b"\t\t<div id=\"classification-footer\" class=\"classification\"><p></p></div>\n\n").expect("write failed");
110    f.write(b"\t</body>\n\n").expect("write failed");
111}
112
113/// The Title Page
114/// 
115/// Assumption: the *section* element is styled in stanhope.css to include a pagebreak-before (we'll use *section* for Sections, too)
116fn compose_html_body_title_page(f:&mut File, p:&Process) {
117    // Crappy old-school HTML tables are still alive
118    f.write(b"\t\t<table id=\"whole-doc\">\n\n").expect("write failed");
119    f.write(b"\t\t\t<thead><tr><td>\n").expect("write failed");
120    f.write(b"\t\t\t\t<div class=\"header-space\">&nbsp;</div>\n").expect("write failed");
121    f.write(b"\t\t\t</td></tr></thead>\n\n").expect("write failed");
122    f.write(b"\t\t<tbody>\n\n").expect("write failed");
123    f.write(b"\t\t\t<tr><td>\n\n").expect("write failed");
124    f.write(b"\t\t\t\t<section class=\"hide-on-screen\">\n").expect("write failed");
125    f.write(b"\t\t\t\t\t<div id=\"title-page\">\n").expect("write failed");
126
127    // Document Title in big text
128    let temp_str = String::from("\t\t\t\t\t\t<div id=\"title-page-title\">") + p.get_title() + "</div>\n";
129    f.write(temp_str.as_bytes()).expect("write failed");
130    
131    // Document Number in big text
132    let proxi = Process::new();
133    let mut process_type = "".to_string();
134    if !(p.get_process_type() == proxi.get_process_type()) { process_type = "<br/>".to_owned() + p.get_process_type(); }
135    let temp_str = String::from("\t\t\t\t\t\t<div id=\"title-page-docno\">") + p.get_number() + &process_type + "<br/>Revision " + p.get_revision() + "</div>\n";
136    f.write(temp_str.as_bytes()).expect("write failed");
137
138    // Author / Reviewer signature block
139    f.write(b"\t\t\t\t\t\t<div id=\"title-page-authors\">\n").expect("write failed");
140    f.write(b"\t\t\t\t\t\t\t<table>\n").expect("write failed");
141    f.write(b"\t\t\t\t\t\t\t\t<thead>\n").expect("write failed");
142    f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
143    let temp_str = String::from("\t\t\t\t\t\t\t\t\t\t<td>Written by:<br/>") + p.get_author() + "</td>\n";
144    f.write(temp_str.as_bytes()).expect("write failed");    
145    let temp_str = String::from("\t\t\t\t\t\t\t\t\t\t<td>Reviewed by:<br/>") + p.get_reviewer() + "</td>\n";
146    f.write(temp_str.as_bytes()).expect("write failed");
147    f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
148    f.write(b"\t\t\t\t\t\t\t\t</thead>\n").expect("write failed");
149    f.write(b"\t\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
150    f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
151    f.write(b"\t\t\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
152    f.write(b"\t\t\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
153    f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
154    f.write(b"\t\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
155    f.write(b"\t\t\t\t\t\t\t</table>\n").expect("write failed");
156    f.write(b"\t\t\t\t\t\t</div>\n").expect("write failed");
157
158    // The content of the "fine print" is defined by the TEMPLATE (it's "content" in the CSS)
159    f.write(b"\t\t\t\t\t\t<div id=\"title-page-fine-print\"></div>\n").expect("write failed");
160
161    // The content of the "No AI" fine print is defined by the TEMPLATE (it's "content" in the CSS)
162    f.write(b"\t\t\t\t\t\t<div id=\"title-page-no-ai\"></div>\n").expect("write failed");
163    
164    // Close out
165    f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
166    f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
167    f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
168}
169
170/// Revisions and Requirements pages
171fn compose_html_body_rev_req_page(f:&mut File, p:&Process) {
172    // The document is one giant table, with <section> as proxy for pagebreak
173    f.write(b"\t\t\t<tr><td>\n\n").expect("write failed");
174    f.write(b"\t\t\t\t<section class=\"\">\n").expect("write failed");
175    f.write(b"\t\t\t\t\t<div id=\"revision-page\">\n").expect("write failed");
176    let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">Revision Table for ").to_string() + p.get_number() + " \"" + p.get_title() + "\"</span>\n";
177    f.write(temp_str.as_bytes()).expect("write failed");
178
179    // Start of the Revision table, which has two columns "Rev" and "Description of Change"
180    f.write(b"\t\t\t\t\t\t<div class=\"revision-table-container\">\n").expect("write failed");
181    f.write(b"\t\t\t\t\t\t\t<table>\n").expect("write failed");
182    f.write(b"\t\t\t\t\t\t\t\t<thead>\n").expect("write failed");
183    f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
184    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"revision-table-rev\">Rev</td>\n").expect("write failed");
185    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"revision-table-change\">Description of Change</td>\n").expect("write failed");
186    f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
187    f.write(b"\t\t\t\t\t\t\t\t</thead>\n").expect("write failed");
188    f.write(b"\t\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
189
190    // Write out each line from data
191    // We'll compare a counter to the total number of Revisions we have in the Vector
192    let mut rev_counter = 0;
193    let len_revs = p.get_all_revisions().len();
194    for rev in p.get_all_revisions().iter().rev() {
195        rev_counter += 1;
196        // The *last* row we write is the current revision, so we wrap the cell contents in another span for stanhope.css styling
197        if rev_counter==len_revs {
198            f.write(b"\t\t\t\t\t\t\t\t\t<tr class=\"current-rev\">\n").expect("write failed"); 
199            let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"revision-table-rev\"><span class=\"current-rev\">".to_string() + &rev.0 + "</span></td>\n";
200            f.write(temp_str.as_bytes()).expect("write failed");
201        } else {
202            f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
203            let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"revision-table-rev\">".to_string() + &rev.0 + "</td>\n";
204            f.write(temp_str.as_bytes()).expect("write failed");
205        }
206        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"revision-table-change\">".to_string() + &rev.1 + "</td>\n";
207        f.write(temp_str.as_bytes()).expect("write failed");
208        f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
209    }
210    // Close out the revision table
211    f.write(b"\t\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
212    f.write(b"\t\t\t\t\t\t\t</table>\n").expect("write failed");
213    f.write(b"\t\t\t\t\t\t</div>\n").expect("write failed");
214    f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
215    f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
216    f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
217
218    // If there are no Verifications in this Process, then this page is done
219    if p.get_all_verifications().len()==0 { return };
220
221    // If there are any Verifications, then we create a new page (<section> has pagebreak)
222    f.write(b"\t\t\t<tr><td>\n\n").expect("write failed");
223    f.write(b"\t\t\t\t<section class=\"\">\n").expect("write failed");
224    f.write(b"\t\t\t\t\t<div id=\"requirements-page\">\n").expect("write failed");
225    let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">Requirements Table for ").to_string() + p.get_number() + " \"" + p.get_title() + "\"</span>\n";
226    f.write(temp_str.as_bytes()).expect("write failed");
227    // Special blurb that mimics an in-process Verification in a Step
228    let temp_str = "\t\t\t\t\t\t<div class=\"requirement\">Successful execution of this procedure produces evidence of verification for the requirements listed in the table below. Therefore, this procedure's execution is particularly important for us to achieve our goal. When you see procedure call-outs in this style of box, that step is what produces this critical evidence. Take the time to understand how these requirements will be verified prior to starting this procedure, and consult this procedure's author (".to_string() + p.get_author() + ") if necessary.</div>\n";
229    f.write(temp_str.as_bytes()).expect("write failed");
230    // Now just generate a table of all requirement verifications in the process document
231    f.write(b"\t\t\t\t\t\t<div class=\"requirements-table-container\">\n").expect("write failed");
232    f.write(b"\t\t\t\t\t\t\t<table>\n").expect("write failed");
233    f.write(b"\t\t\t\t\t\t\t\t<thead>\n").expect("write failed");
234    f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
235    // Four columns: Requirement ID (RID), Text of the Requirement itself, Method of Verification, and the Step number in this process that identifies the Verification step
236    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-rid\">RID</td>\n").expect("write failed");
237    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-text\">Text of Requirement</td>\n").expect("write failed");
238    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-method\">Verif. Method</td>\n").expect("write failed");
239    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-step\">Step</td>\n").expect("write failed");
240    f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
241    f.write(b"\t\t\t\t\t\t\t\t</thead>\n").expect("write failed");
242    f.write(b"\t\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
243    // Simply pull the data out and fill the table
244    for req in p.get_all_verifications().iter() {
245        f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
246        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-rid\">".to_string() + &req.0.get_id() + "</td>\n";
247        f.write(temp_str.as_bytes()).expect("write failed");
248        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-text\">".to_string() + &req.0.get_text() + "</td>\n";
249        f.write(temp_str.as_bytes()).expect("write failed");
250        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-method\">".to_string() + &req.0.get_method() + "</td>\n";
251        f.write(temp_str.as_bytes()).expect("write failed");
252        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"requirements-table-step\">".to_string() + &req.1 + "</td>\n";
253        f.write(temp_str.as_bytes()).expect("write failed");
254        f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
255    }
256    // Close out the table and the section/page
257    f.write(b"\t\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
258    f.write(b"\t\t\t\t\t\t\t</table>\n").expect("write failed");
259    f.write(b"\t\t\t\t\t\t</div>\n").expect("write failed");
260    f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
261    f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
262    f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
263}
264
265/// Table of Contents page, and the Subject/Product Resources page...
266/// 
267/// Currently, if there are any verifications in the document at all, they are gathered up to the TOC page as well.
268/// 
269/// We'll create some special elements that look somewhat like those that appear in-process (roundrect) but alternate colors
270fn compose_html_body_toc(f:&mut File,p:&Process,s:&Vec<Section>) {
271
272    /// Round robin color picker for alternating steps in the Resource blob
273    fn pick_color(index:usize) -> String {
274        // If we want three colors, etc.
275        match index % 3 {
276            2 => String::from("border: 0; background-color: #FFFFFF; color: #5566AA;"),
277            1 => String::from("border: 0; background-color: #FFFFFF; color: #5566AA;"),
278            0 => String::from("border: 0; background-color: #FFFFFF; color: #5566AA;"),
279            _ => String::from("border: 0; background-color: #AA99DD; color: #5566AA;"),
280        }
281        /*
282        match index % 3 {
283            2 => String::from("border: 2px solid #5566AA; background-color: #CCDDFF; color: #5566AA;"),
284            1 => String::from("border: 2px solid #5566AA; background-color: #CCBBFF; color: #5566AA;"),
285            0 => String::from("border: 2px solid #5566AA; background-color: #AABBEE; color: #5566AA;"),
286            _ => String::from("border: 2px solid #5566AA; background-color: #AA99DD; color: #5566AA;"),
287        }
288        */
289    }
290
291    // Table of Contents
292    f.write(b"\t\t\t<tr><td>\n\n").expect("write failed");
293    f.write(b"\t\t\t\t<section class=\"\">\n").expect("write failed");
294    f.write(b"\t\t\t\t\t<div id=\"toc\">\n").expect("write failed");
295    let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">").to_string() + p.get_number() + ", Rev " + p.get_revision() + " \"" + p.get_title() + "\"</span>\n";
296    f.write(temp_str.as_bytes()).expect("write failed");
297    // This TOC is a bulleted list with links to document anchors
298    f.write(b"\t\t\t\t\t\t<ul style=\"toc-section\">\n").expect("write failed");
299    for (ii,sec) in s.iter().enumerate() { 
300        let temp_str = String::from("\t\t\t\t\t\t\t<li><a class=\"pageref\" href=\"#Section") + &(ii+1).to_string() + "\">Section " + &(ii+1).to_string() + "</a>: " + sec.get_title() + "</a></li>\n";
301        f.write(temp_str.as_bytes()).expect("write failed");
302        f.write(b"\t\t\t\t\t\t\t\t<ul style=\"toc-step\">\n").expect("write failed");
303        for (jj,stp) in sec.get_all_steps().iter().enumerate() {
304            let temp_str = String::from("\t\t\t\t\t\t\t\t\t<li><a class=\"pageref\" href=\"#Step") + &(ii+1).to_string() + "." + &(jj+1).to_string() + "\">Step " + &(ii+1).to_string() + "." + &(jj+1).to_string() + "</a>: " + stp.get_text() + "</a></li>\n";
305            f.write(temp_str.as_bytes()).expect("write failed");
306        }
307        f.write(b"\t\t\t\t\t\t\t\t</ul>\n").expect("write failed");
308    }
309    f.write(b"\t\t\t\t\t\t</ul>\n\n").expect("write failed");
310    f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
311
312    // Roles - see Roles.css
313    f.write(b"\t\t\t\t\t<div id=\"roles\">\n").expect("write failed");
314    f.write(b"\t\t\t\t\t\t<span class=\"toc-title\">Personnel Performing this Process, by Named Role:</span>\n").expect("write failed");
315    f.write(b"\t\t\t\t\t\t<table id=\"roles-table\">\n").expect("write failed");
316    f.write(b"\t\t\t\t\t\t\t<thead id=\"roles-header\">\n").expect("write failed");
317    f.write(b"\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
318    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-title\">Role in this Process</td>\n").expect("write failed");
319    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-name\">Name of Person</td>\n").expect("write failed");
320    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-initials\">Initials</td>\n").expect("write failed");
321    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-contact\">Contact Information</td>\n").expect("write failed");
322    f.write(b"\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
323    f.write(b"\t\t\t\t\t\t\t</thead>\n").expect("write failed");
324    f.write(b"\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
325    let num_roles:usize = 10;
326    for ii in 1..(num_roles+1) {
327        let temp_str = String::from("\t\t\t\t\t\t\t\t<tr id=\"role-".to_string() + &format!("{:0>2}",ii) + "\">\n");
328        f.write(temp_str.as_bytes()).expect("write failed");
329        f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-title\"></td>\n").expect("write failed");
330        f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-name\"></td>\n").expect("write failed");
331        f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-initials\"></td>\n").expect("write failed");
332        f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"role-contact\"></td>\n").expect("write failed");
333        f.write(b"\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
334    }
335    f.write(b"\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
336    f.write(b"\t\t\t\t\t\t</table>\n").expect("write failed");
337    f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
338
339    // Various warnings
340    // TODO: robustify this by generalizing templates.
341    f.write(b"\t\t\t\t\t<div class=\"power-on\"><img/><span class=\"power-on-text\"></span></div>\n").expect("write failed");
342    f.write(b"\t\t\t\t\t<div class=\"rf-emission\"><img/><span class=\"rf-emission-text\"></span></div>\n").expect("write failed");
343    f.write(b"\t\t\t\t\t<div class=\"hazardous-ops\"><img/><span class=\"hazardous-ops-text\"></span></div>\n").expect("write failed");
344    f.write(b"\t\t\t\t\t<div class=\"esd\"><img/><span class=\"esd-text\"></span></div>\n").expect("write failed");
345    f.write(b"\t\t\t\t\t<div class=\"fod\"><img/><span class=\"fod-text\"></span></div>\n").expect("write failed");
346    f.write(b"\t\t\t\t\t<div class=\"welding\"><img/><span class=\"welding-text\"></span></div>\n").expect("write failed");
347    f.write(b"\t\t\t\t\t<div class=\"comsec\"><img/><span class=\"comsec-text\"></span></div>\n").expect("write failed");
348    f.write(b"\t\t\t\t\t<div class=\"cleanroom\"><img/><span class=\"cleanroom-text\"></span></div>\n").expect("write failed");
349    f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
350    f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
351
352    // Only make the next page if you have a nonzero number of
353    // Objectives OR
354    // Out-of-Scopes OR
355    // Resources OR
356    // A defined "subject" OR
357    // A defined "product"
358    if p.get_all_objectives().len() > 0 || p.get_all_out_of_scopes().len() > 0 || p.get_all_resources().len() > 0 || !(*p.get_subject()==String::from("N/A")) || !(*p.get_product()==String::from("N/A")) {
359        // New page for Subject/Product and Resources
360        f.write(b"\t\t\t<tr><td>\n\n").expect("write failed");
361        f.write(b"\t\t\t\t<section class=\"\">\n").expect("write failed");
362        
363        let all_objectives = p.get_all_objectives();
364        if all_objectives.len() > 0 {
365            // List of Objectives
366            f.write(b"\t\t\t\t\t<div id=\"all-objectives\">\n").expect("write failed");
367            let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">This process has ") + &all_objectives.len().to_string() + " stated objective" + match all_objectives.len() {1=>"",_=>"s"} + ":</span>\n";
368            f.write(temp_str.as_bytes()).expect("write failed");
369            f.write(b"\t\t\t\t\t\t<ol class=\"objective-list\">\n").expect("write failed");
370            for (_jj,(obj,snum)) in all_objectives.iter().enumerate() {
371                let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &obj + " (" + &snum + ")</li>\n";
372                f.write(temp_str.as_bytes()).expect("write failed");
373            }
374            f.write(b"\t\t\t\t\t\t</ol>\n").expect("write failed");
375            f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
376        };
377
378        let all_out_of_scopes = p.get_all_out_of_scopes();
379        if all_out_of_scopes.len() > 0 {
380            // List of Objectives
381            f.write(b"\t\t\t\t\t<div id=\"all-out-of-scopes\">\n").expect("write failed");
382            let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">This process has ") + &all_out_of_scopes.len().to_string() + " <em>Out Of Scope</em> declaration" + match all_out_of_scopes.len() {1=>"",_=>"s"} + ":</span>\n";
383            f.write(temp_str.as_bytes()).expect("write failed");
384            f.write(b"\t\t\t\t\t\t<ol class=\"out-of-scope-list\">\n").expect("write failed");
385            for (_jj,(oos,snum)) in all_out_of_scopes.iter().enumerate() {
386                let temp_str = String::from("\t\t\t\t\t\t\t<li>") + &oos + " (" + &snum + ")</li>\n";
387                f.write(temp_str.as_bytes()).expect("write failed");
388            }
389            f.write(b"\t\t\t\t\t\t</ol>\n").expect("write failed");
390            f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
391        };
392
393        // Subject/Product logic and writing
394        f.write(b"\t\t\t\t\t<div id=\"subject-product\">\n").expect("write failed");
395        match p.get_product().as_str() {
396            ""|"N/A" => {
397                match p.get_subject().as_str() {
398                    ""|"N/A"|"NO SUBJECT IN EBML FILE" => (),
399                    _ => {
400                        let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">\"") + p.get_title() + "\" applies to <em>" + p.get_subject() + "</em></span><br/>\n";
401                        f.write(temp_str.as_bytes()).expect("write failed");
402                        let temp_str = String::from("\t\t\t\t\t\t<div class=\"subject-product-image-container\"><img src=\"") + p.get_subject_image() + "\"/></div>\n";
403                        f.write(temp_str.as_bytes()).expect("write failed");
404                    },
405                }
406            },
407            _ => {
408                let temp_str = String::from("\t\t\t\t\t\t<span class=\"toc-title\">\"") + p.get_title() + "\" applies to <em>" + p.get_subject() + "</em> to produce <em>" + p.get_product() +"</em></span><br/>\n";
409                f.write(temp_str.as_bytes()).expect("write failed");
410                let temp_str = String::from("\t\t\t\t\t\t<div class=\"subject-product-image-container\"><img src=\"") + p.get_subject_image() + "\"/><span class=\"arrow\">&rarr;</span><img src=\"" + p.get_product_image() + "\"/></div>\n";
411                f.write(temp_str.as_bytes()).expect("write failed");
412            },
413        };
414        f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
415        
416        // Resource blob
417        if p.get_all_resources().len() > 0 {
418            f.write(b"\t\t\t\t\t\t<div class=\"resources-container\">\n").expect("write failed");
419            let temp_str = "\t\t\t\t\t\t<span class=\"resources-text\">".to_string() + &p.get_all_resources().len().to_string() + " named resources are identified to perform this process:</span><br/>\n";
420            f.write(temp_str.as_bytes()).expect("write failed");
421
422            // How we're using the color picker: increment the "index" and let the function apply the remainder function
423            let mut step_num = "";
424            let mut index:usize = 0;
425            for (resource,resource_step) in p.get_all_resources() {
426                if !(step_num==resource_step) { index += 1; };
427                step_num = resource_step;
428                let temp_str = "\t\t\t\t\t\t\t<div class=\"resource-bubble\" style=\"".to_string() + &pick_color(index) + ";\"><span class=\"resource-bubble-step\">" + &resource_step + "</span><br/>" + &resource.get_name() + "</div>\n";
429                f.write(temp_str.as_bytes()).expect("write failed");
430            }
431            f.write(b"\t\t\t\t\t\t</div><div style=\"clear:both;\"></div>\n").expect("write failed");
432
433            // Calibration table
434            if p.get_all_calibrated_resources().len() > 0 {
435                f.write(b"\t\t\t\t\t<div id=\"calibration\">\n").expect("write failed");
436                f.write(b"\t\t\t\t\t\t<span class=\"toc-title\">Equipment Calibration Status</span>\n").expect("write failed");
437                f.write(b"\t\t\t\t\t\t<table id=\"calibration-table\">\n").expect("write failed");
438                f.write(b"\t\t\t\t\t\t\t<thead id=\"calibration-header\">\n").expect("write failed");
439                //f.write(b"\t\t\t\t\t\t\t\t<tr><td class=\".nocell\"></td><td colspan=\"2\" class=\".whitecell\">Today's Date: </td></tr>\n").expect("write failed");
440                f.write(b"\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
441                f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-resource\">Equipment</td>\n").expect("write failed");
442                f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-num\">Model Number</td>\n").expect("write failed");
443                f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-serial\">Serial</td>\n").expect("write failed");
444                f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-date\">Last Cal Date</td>\n").expect("write failed");
445                f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-due\">Valid Until</td>\n").expect("write failed");
446                f.write(b"\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
447                f.write(b"\t\t\t\t\t\t\t</thead>\n").expect("write failed");
448                f.write(b"\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
449                for resource in p.get_all_calibrated_resources() {
450                    let temp_str = String::from("\t\t\t\t\t\t\t\t<tr>\n");
451                    f.write(temp_str.as_bytes()).expect("write failed");
452                    //f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-resource\"></td>\n").expect("write failed");
453                    let temp_str = "\t\t\t\t\t\t\t\t\t<td class=\"calibration-resource\">".to_string() + &resource.get_name() + "</td>\n";
454                    f.write(temp_str.as_bytes()).expect("write failed");
455                    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-num\"></td>\n").expect("write failed");
456                    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-serial\"></td>\n").expect("write failed");
457                    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-date\"></td>\n").expect("write failed");
458                    f.write(b"\t\t\t\t\t\t\t\t\t<td class=\"calibration-due\"></td>\n").expect("write failed");
459                    f.write(b"\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
460                }
461                f.write(b"\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
462                f.write(b"\t\t\t\t\t\t\t<tfoot>\n").expect("write failed");
463                f.write(b"\t\t\t\t\t\t\t\t<tr><td colspan=\"3\">If any equipment is within 30 days of needing recalibration, notify your QA rep. <strong>Today's Date:</strong></td></tr>\n").expect("write failed");
464                f.write(b"\t\t\t\t\t\t\t</tfoot>\n").expect("write failed");
465                f.write(b"\t\t\t\t\t\t</table>\n").expect("write failed");
466                f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
467            }
468
469
470        }
471        f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
472        f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
473        // end resource blob
474    }
475    /* end if */
476
477    // NCRs - Non-Conformances
478    f.write(b"\t\t\t<tr id=\"non-conformances\"><td>\n\n").expect("write failed");
479    f.write(b"\t\t\t\t<section class=\"\">\n").expect("write failed");
480    f.write(b"\t\t\t\t\t<span class=\"toc-title\">Log of Non-Conformances during this execution:</span>\n").expect("write failed");
481    f.write(b"\t\t\t\t\t<table>\n").expect("write failed");
482    f.write(b"\t\t\t\t\t\t<thead>\n").expect("write failed");
483    f.write(b"\t\t\t\t\t\t\t<tr>\n").expect("write failed");
484    f.write(b"\t\t\t\t\t\t\t\t<td class=\"text-rotated\">Time</td>\n").expect("write failed");
485    f.write(b"\t\t\t\t\t\t\t\t<td class=\"text-rotated\">Step</td>\n").expect("write failed");
486    f.write(b"\t\t\t\t\t\t\t\t<td class=\"text-rotated\">Record</td>\n").expect("write failed");
487    f.write(b"\t\t\t\t\t\t\t\t<td>Brief Summary of Observation(s) that Deviated from Documented Expectations</td>\n").expect("write failed");
488    f.write(b"\t\t\t\t\t\t\t</tr>\n").expect("write failed");
489    f.write(b"\t\t\t\t\t\t</thead>\n").expect("write failed");
490    f.write(b"\t\t\t\t\t\t<tbody>\n").expect("write failed");
491    f.write(b"\t\t\t\t\t\t\t<tr>\n").expect("write failed");
492    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
493    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
494    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
495    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
496    f.write(b"\t\t\t\t\t\t\t</tr>\n").expect("write failed");
497    f.write(b"\t\t\t\t\t\t\t<tr>\n").expect("write failed");
498    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
499    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
500    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
501    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
502    f.write(b"\t\t\t\t\t\t\t</tr>\n").expect("write failed");
503    f.write(b"\t\t\t\t\t\t\t<tr>\n").expect("write failed");
504    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
505    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
506    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
507    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
508    f.write(b"\t\t\t\t\t\t\t</tr>\n").expect("write failed");
509    f.write(b"\t\t\t\t\t\t\t<tr>\n").expect("write failed");
510    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
511    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
512    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
513    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
514    f.write(b"\t\t\t\t\t\t\t</tr>\n").expect("write failed");
515    f.write(b"\t\t\t\t\t\t\t<tr>\n").expect("write failed");
516    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
517    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
518    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
519    f.write(b"\t\t\t\t\t\t\t\t<td></td>\n").expect("write failed");
520    f.write(b"\t\t\t\t\t\t\t</tr>\n").expect("write failed");
521    f.write(b"\t\t\t\t\t\t</tbody>\n").expect("write failed");
522    f.write(b"\t\t\t\t\t</table>\n").expect("write failed");
523    f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
524    f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
525
526    
527}
528
529/// Write every Section in the Process, sequentially. Include Steps within Sections and SubSteps within Steps.
530fn compose_html_body_section(f:&mut File, p:&Process, all_sections:&Vec<Section>) {
531    
532    // Count the Sections as we go, beceuse sequential processes deserve sequential indenting and numbering
533    let mut counter = 0;
534    for section in all_sections {
535        // Each Section is cleverly its own *section* element... brilliant!
536        f.write(b"\t\t\t<tr><td>\n\n").expect("write failed");
537        f.write(b"\t\t\t\t<section>\n").expect("write failed");
538        
539        // Count the sections as we go
540        // TODO: modernize this by enumerating an iterator for all_sections, if Rust lets us use iterators for our own Vec<Section>
541        counter += 1;
542
543        // Print out the title of the section
544        let temp_str = "\t\t\t\t\t<div class=\"section-title\"><a name=\"Section".to_string() + &counter.to_string() + "\"></a>Section " + &counter.to_string() + ": " + &section.get_title() + "</div>\n";
545        f.write(temp_str.as_bytes()).expect("write failed");
546
547        // Iterate over every Step in the Section
548        for (ii,step) in section.get_all_steps().iter().enumerate() {
549
550            // Use the text stored at the Step level at the top of the Step
551            let temp_str = "\t\t\t\t\t<div class=\"step\"><span class=\"step-text\"><a name=\"Step".to_string() + &counter.to_string() + "." + &{ii+1}.to_string() + "\"></a>Step " + &counter.to_string() + "." + &{ii+1}.to_string() + ": " + &step.get_text() + "</span><br/>\n";
552            f.write(temp_str.as_bytes()).expect("write failed");
553
554            // Resource blob goes at the top of each Step, so that the operator can be full-kit before starting the Step
555            if step.get_resources().len() > 0 {
556                f.write(b"\t\t\t\t\t\t\t<div class=\"resources-container\">\n").expect("write failed");
557                let temp_str = "\t\t\t\t\t\t\t\t<span class=\"resources-text\">".to_string() + &step.get_resources().len().to_string() + " named resources are identified to perform this step:</span><br/>\n";
558                f.write(temp_str.as_bytes()).expect("write failed");
559                for resource in step.get_resources() {
560                    let temp_str = "\t\t\t\t\t\t\t\t<div class=\"resource-bubble\">".to_string() + &resource.get_name() + "</div>\n";
561                    f.write(temp_str.as_bytes()).expect("write failed");
562                }
563                f.write(b"\t\t\t\t\t\t\t</div><div style=\"clear:both;\"></div>\n").expect("write failed");
564            }
565            // end resource blob
566
567            // Iterate over all of the SubSteps in the Step, and use other functions in this crate to write out accordingly
568            for (_jj,sub_step) in step.get_all_sub_steps().iter().enumerate() {
569                match sub_step {
570                    SubStep::ActionSequence(action_sequence) => compose_html_body_section_step_action_sequence(f,&action_sequence),
571                    SubStep::Command(command_text) => compose_html_body_section_step_command(f,&command_text),
572                    SubStep::Image(image_file,image_text) => compose_html_body_section_step_image(f,&image_file,&image_text,section.get_relative_path()),
573                    SubStep::Warning(warning_text) => compose_html_body_section_step_warning(f,&warning_text),
574                    SubStep::Verification(requirement) => compose_html_body_section_step_verification(f,&p.get_author(),&requirement),
575                    // Do nothing for Resource type SubSteps, because we've printed them at the top of the Step
576                    // TODO: add a feature later to also do something in-place where the Resource was identified within the Step...
577                    SubStep::Resource(_resource) => (),
578                    SubStep::Context(text) => compose_html_body_section_step_context(f,&text),
579                    SubStep::Objective(objective_text) => compose_html_body_section_step_objective(f,&objective_text),
580                    SubStep::OutOfScope(out_of_scope_text) => compose_html_body_section_step_out_of_scope(f,&out_of_scope_text),
581                    SubStep::Table(table) => compose_html_body_section_step_table(f,&table),
582                }
583            }
584            // close the Step div
585            f.write(b"\t\t\t\t\t</div>\n").expect("write failed");
586        }
587        // close the section
588        f.write(b"\t\t\t\t</section>\n\n").expect("write failed");
589        f.write(b"\t\t\t</td></tr>\n\n").expect("write failed");
590    }
591}
592
593/// Print ActionSequence type SubStep
594fn compose_html_body_section_step_action_sequence(f:&mut File, act_seq:&Vec<Action>) {
595    // ActionSequence prints as a table (tables!)
596    f.write(b"\t\t\t\t\t\t<div class=\"action-sequence\">\n").expect("write failed");
597    f.write(b"\t\t\t\t\t\t\t<table>\n").expect("write failed");
598    f.write(b"\t\t\t\t\t\t\t\t<thead>\n").expect("write failed");
599    f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
600    // Three columns: Action to perform, Expected result, and Two-Party Verification
601    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-action\">Action to perform</td>\n").expect("write failed");
602    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-expected\">Expected</td>\n").expect("write failed");
603    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-result\">Result</td>\n").expect("write failed");
604    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-tpv\">Done</td>\n").expect("write failed");
605    f.write(b"\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-tpv\">TPV</td>\n").expect("write failed");
606    f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
607    f.write(b"\t\t\t\t\t\t\t\t</thead>\n").expect("write failed");
608    f.write(b"\t\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
609
610    // one of these for each action
611    for act in act_seq {
612        f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
613        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-action\">".to_string() + act.get_perform() + "</td>\n";
614        f.write(temp_str.as_bytes()).expect("write failed");
615        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-expected\">".to_string() + act.get_expect() + "</td>\n";
616        f.write(temp_str.as_bytes()).expect("write failed");
617        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-result\">".to_string() + "</td>\n";
618        f.write(temp_str.as_bytes()).expect("write failed");
619        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-tpv\">".to_string() + "<span class=\"action-checkbox\">&#x25A2;</span>" + "</td>\n";
620        f.write(temp_str.as_bytes()).expect("write failed");
621        let temp_str = "\t\t\t\t\t\t\t\t\t\t<td class=\"action-sequence-tpv\">".to_string() + { if !act.get_tpv() { "N/A" } else { "<span class=\"action-checkbox\">&#x25A2;</span>" }} + "</td>\n";
622        f.write(temp_str.as_bytes()).expect("write failed");
623        f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
624    }
625
626    // close the table
627    f.write(b"\t\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
628    f.write(b"\t\t\t\t\t\t\t</table>\n").expect("write failed");
629    f.write(b"\t\t\t\t\t\t</div>\n").expect("write failed");
630}
631
632/// Print Command type SubStep
633fn compose_html_body_section_step_command(f:&mut File, c:&String) {
634    // Simple div and span combination for formatting in stanhope.css
635    let temp_str = "\t\t\t\t\t\t<div class=\"command\"><span class=\"command-text\">".to_string() + &c.to_string() + "</span></div>\n";
636    f.write(temp_str.as_bytes()).expect("write failed");
637}
638
639/// Print Image type SubStep
640fn compose_html_body_section_step_image(f:&mut File, img:&String, txt:&String, path:&String) {
641    // There's a container Div, and inside that there's the image, and below the image there's the caption
642    // The container Div can be stylized in stanhope.css to be centered (for instance), with a border, etc.
643    // The container can also be styled to have maximum width or height, etc.
644    let temp_str = "\t\t\t\t\t\t<div class=\"image\"><div class=\"image-container\"><img src=\"".to_string() + &path.to_string() + &img.to_string() + "\" onerror=\"this.onerror=null; this.src='../assets/placeholderImage-small.png'\"/><br/><span class=\"image-text\">" + &txt.to_string() + "</span></div></div>\n";
645    f.write(temp_str.as_bytes()).expect("write failed");
646}
647
648/// Print Warning type SubStep
649fn compose_html_body_section_step_warning(f:&mut File, w:&String) {
650    // Simple div and span combination for formatting in stanhope.css
651    let temp_str = "\t\t\t\t\t\t<div class=\"warning\"><span class=\"warning-text\">".to_string() + &w.to_string() + "</span></div>\n";
652    f.write(temp_str.as_bytes()).expect("write failed");
653}
654
655/// Print Verification type SubStep
656fn compose_html_body_section_step_verification(f:&mut File, author:&String, r:&Requirement) {
657    // Simple div and span combination for formatting in stanhope.css, with additional Requirement Verificaiton boilerplate text
658    let temp_str = "\t\t\t\t\t\t<div class=\"requirement\">This step produces the evidence of verification for this requirement:<br/><span class=\"requirement-rid\">".to_string() + &r.get_id() + ": \"" + &r.get_text() + "\" (Verify by " + &r.get_method() + ")</span><br/>Please ensure the evidence produced passes the requirement before proceding. This might require consulting this procedure's author, " + author + ".</div>\n";
659    f.write(temp_str.as_bytes()).expect("write failed");
660}
661
662/// Print Context type SubStep
663fn compose_html_body_section_step_context(f:&mut File, text:&String) {
664    // Simple div and span combination for formatting in stanhope.css
665    let temp_str = "\t\t\t\t\t\t<div class=\"context\"><span class=\"context-text\">".to_string() + &text.to_string() + "</span></div>\n";
666    f.write(temp_str.as_bytes()).expect("write failed");
667}
668
669/// Print Objective type SubStep
670fn compose_html_body_section_step_objective(f:&mut File, objective_text:&String) {
671    // Simple div and span combination for formatting in stanhope.css, with additional Objective text
672    let temp_str = "\t\t\t\t\t\t<div class=\"objective\">".to_string() + objective_text + "</div>\n";
673    f.write(temp_str.as_bytes()).expect("write failed");
674}
675
676/// Print Out of Scope type SubStep
677fn compose_html_body_section_step_out_of_scope(f:&mut File, out_of_scope_text:&String) {
678    // Simple div and span combination for formatting in stanhope.css, with additional Out Of Scope text
679    let temp_str = "\t\t\t\t\t\t<div class=\"out-of-scope\">".to_string() + out_of_scope_text + "</div>\n";
680    f.write(temp_str.as_bytes()).expect("write failed");
681}
682
683/// Print an arbitrary table in its own div element, based on the contents of a "Table" struct (including a caption)
684fn compose_html_body_section_step_table(f:&mut File, table:&Table) {
685
686    let (rows,cols) = table.get_size();
687
688    // Only write a table if there is a table...
689    if rows > 0 {
690
691        f.write(b"\t\t\t\t\t\t<div class=\"csv-table-container\">\n").expect("write failed");
692        f.write(b"\t\t\t\t\t\t\t<table>\n").expect("write failed");
693
694        let temp_str = "\t\t\t\t\t\t\t\t<caption>".to_string() + table.get_caption() + "</caption>\n";
695        f.write(temp_str.as_bytes()).expect("write failed");
696
697        f.write(b"\t\t\t\t\t\t\t\t<thead>\n").expect("write failed");
698        f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
699        let first_row = table.get_row(0);
700        for jj in 0..cols {
701
702            let temp_str = "\t\t\t\t\t\t\t\t\t\t<th>".to_string() + &first_row[jj] + "</th>\n";
703            f.write(temp_str.as_bytes()).expect("write failed");
704        }
705        f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
706        f.write(b"\t\t\t\t\t\t\t\t</thead>\n").expect("write failed");
707        f.write(b"\t\t\t\t\t\t\t\t<tbody>\n").expect("write failed");
708        
709        for ii in 1..rows {
710            let this_row = table.get_row(ii);
711            f.write(b"\t\t\t\t\t\t\t\t\t<tr>\n").expect("write failed");
712            for jj in 0..cols {
713                let temp_str = "\t\t\t\t\t\t\t\t\t\t<td>".to_string() + &this_row[jj] + "</td>\n";
714                f.write(temp_str.as_bytes()).expect("write failed");
715            }
716            f.write(b"\t\t\t\t\t\t\t\t\t</tr>\n").expect("write failed");
717        }
718        
719        f.write(b"\t\t\t\t\t\t\t\t</tbody>\n").expect("write failed");
720        f.write(b"\t\t\t\t\t\t\t</table>\n").expect("write failed");
721        f.write(b"\t\t\t\t\t\t</div>\n").expect("write failed");
722    }
723}
724
725/// Close out the sections, and include the process document's header and footer banners
726fn compose_html_body_end_of_sections_and_banners(f:&mut File, p:&Process) {
727    f.write(b"\t\t</tbody>\n\n").expect("write failed");
728    f.write(b"\t\t\t<tfoot><tr><td>\n").expect("write failed");
729    f.write(b"\t\t\t\t<div class=\"footer-space\">&nbsp;</div>\n").expect("write failed");
730    f.write(b"\t\t\t</td></tr></tfoot>\n").expect("write failed");
731    f.write(b"\t\t</table>\n\n").expect("write failed");
732
733    // The header div is hard-programmed here to be full-width. Stanhope.css configures the height
734    f.write(b"\t\t<div class=\"header\">\n").expect("write failed");
735    f.write(b"\t\t\t<table style=\"margin:0; padding:0; width:100%;\">\n").expect("write failed");
736    f.write(b"\t\t\t\t<tbody style=\"margin:0; padding:0;\">\n").expect("write failed");
737    f.write(b"\t\t\t\t\t<tr style=\"vertical-align: top; margin:0;\">\n").expect("write failed");
738
739    // Split the width of the header with... ANOTHER TABLE!
740    // Logo image goes here in an empty *img* tag. Stanhope.css has a placeholder logo, but Template files can change the 'content' of this img to change what displays.
741    // The logo/header image should be in ../assets/ for reusability
742    let temp_str = "\t\t\t\t\t\t<td id=\"header-left\"><p class=\"header-p\"><img/><span style=\"font-size:1.3em; font-weight:bold;\">".to_string() + p.get_number() + ", Rev " + p.get_revision() + "</span><br/>Written by: " + p.get_author() + "<br/>Reviewed by: " + p.get_reviewer() + "</p></td>\n";
743    f.write(temp_str.as_bytes()).expect("write failed");
744    // The right side of the header
745    let temp_str = "\t\t\t\t\t\t<td id=\"header-right\"><p class=\"header-p\"><span style=\"font-size:1.3em; font-weight:bold;\">".to_string() + p.get_title() + "</span><br/>Applies to " + p.get_subject() + " to produce " + p.get_product() + "<br/>" + &p.get_all_resources().len().to_string() + " named resources are identified to perform this process</p></td>\n";
746    f.write(temp_str.as_bytes()).expect("write failed");
747    // Close out header
748    f.write(b"\t\t\t\t\t</tr>\n").expect("write failed");
749    f.write(b"\t\t\t\t</tbody>\n").expect("write failed");
750    f.write(b"\t\t\t</table>\n").expect("write failed");
751    f.write(b"\t\t</div>\n").expect("write failed");
752
753    // Footer is also a... TABLE!!
754    f.write(b"\t\t<div class=\"footer\">\n").expect("write failed");
755    f.write(b"\t\t\t<table style=\"margin:0; padding:0; width:100%;\">\n").expect("write failed");
756    f.write(b"\t\t\t\t<tbody style=\"margin:0; padding:0; width:100%\">\n").expect("write failed");
757    f.write(b"\t\t\t\t\t<tr style=\"vertical-align: top; margin:0;\">\n").expect("write failed");
758    // Similar to header
759    let temp_str = "\t\t\t\t\t\t<td width=\"100%\" style=\"margin:0; padding:0;\"><p class=\"header-p\"><!--<a href=\"https://strativusgroup.com\">--><img/>Produced efficiently with stanhope. See this document's first page for restrictions and distribution statements.<!--</a>--><span>".to_string() + "</p></td>\n";
760    f.write(temp_str.as_bytes()).expect("write failed");
761    // Middle section
762    let temp_str = "\t\t\t\t\t\t<td><p class=\"header-p\"><span>".to_string() + "</span><br/></p></td>\n";
763    f.write(temp_str.as_bytes()).expect("write failed");
764    /*
765    // Right-hand section
766    let temp_str = "\t\t\t\t\t\t<td width=\"10%\"><p class=\"header-p\"><span class=\"hide-on-screen\" id=\"pagenum\"></span><br/></p></td>\n".to_string();
767    f.write(temp_str.as_bytes()).expect("write failed");
768    */
769    
770    // close out the banners
771    f.write(b"\t\t\t\t\t</tr>\n").expect("write failed");
772    f.write(b"\t\t\t\t</tbody>\n").expect("write failed");
773    f.write(b"\t\t\t</table>\n").expect("write failed");
774    f.write(b"\t\t</div>\n").expect("write failed");   
775}