Software Release
About the Release of Occam's Reader 1.1
Thank you for your interest in the Occam's Reader 1.1 system!
The members of the Occam's Reader Pilot Group (Texas Tech University, The University of Hawai'i at Manoa, and the Greater Western Library Alliance) feel very strongly about our desire to give back to the library community at-large through this source code release, using a Creative Commons Attribution 4.0 International License. We also hope to contribute to the education of the library world by taking the time to explain how the system works and why certain choices were made.
Please bear with us as we work to improve this release by adding more detail to each part of the system.
The Occam's Reader Pilot Group
Texas Tech University:
- Kenny Ketner
- Ryan Litsey
University of Hawai'i at Manoa:
- Naomi Chow
- Erin Kim
- Wing Leung
- Arthur Shum
Greater Western Library Alliance:
- Joni Blake
- Anne McKee
Legal Notes
Occam's Reader 1.1 by Occam's Reader Pilot Group is licensed under a Creative Commons Attribution 4.0 International License. Based on a work at http://occamsreader.org/release. Permissions beyond the scope of this license may be available at http://occamsreader.org/release
System Overview
The Occam's Reader system consists of many distinct parts:
- Freely available software prerequisites not distributed with Occam's Reader
- The image conversion software
- The ILLiad add-on and its supporting files
- The Occam's Reader web pages that render within the ILLiad add-on
- The web viewer for patrons
Each part will be discussed below.
Software prerequisites
The following programs are prerequisites, installed separately. These programs should be present on all ILL staff computers that will be running Occam's Reader. To avoid any licensing conflicts, the Occam's Reader Pilot Group does not distribute these products.
- 7-Zip
- ImageMagick
- GhostScript
Image conversion software
Occam's Reader conversion software is a .NET executable distributed to ILL staff at particfipating institutions. In order to avoid any licensing conflicts with Microsoft .NET, we provide the source code only, pasted below.
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace OccamsReader
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
comboDPI.Items.Add("High (Default)");
comboDPI.Items.Add("Low");
comboDPI.Items.Add("Medium");
comboDPI.Items.Add("Very High");
//comboDPI.Items.Add("Default");
comboDPI.SelectedIndex = 0;
comboQuality.Items.Add("Low");
comboQuality.Items.Add("Medium");
comboQuality.Items.Add("High");
comboQuality.Items.Add("Default");
comboQuality.SelectedIndex = 0;
comboFontName.Items.Add("Arial");
comboFontName.Items.Add("Courier New");
comboFontName.Items.Add("Georgia");
comboFontName.Items.Add("Tahoma");
comboFontName.SelectedIndex = 0;
comboFontSize.Items.Add("12 Point");
comboFontSize.Items.Add("10 Point");
comboFontSize.Items.Add("8 Point");
comboFontSize.Items.Add("14 Point");
comboFontSize.SelectedIndex = 0;
txtFilename.Text = " ";
}
private void btnOpen_Click(object sender, EventArgs e)
{
// Show the dialog and get result.
DialogResult result = openFileDialog1.ShowDialog();
if (result == DialogResult.OK) // Test result.
{
}
//System.IO.StreamReader sr = new System.IO.StreamReader(openFileDialog1.FileName);
//MessageBox.Show(sr.ReadToEnd());
//MessageBox.Show(openFileDialog1.FileName);
//sr.Close();
lblFilename.Text = openFileDialog1.FileName;
//Console.WriteLine(result); // <-- For debugging use only.
}
private void lblFilename_Click(object sender, EventArgs e)
{
//MessageBox.Show(Properties.Settings.Default.ILLiadDirectory);
//MessageBox.Show(OccamsReader.Properties.Settings.Default.ILLNumber);
}
private void btnGenerate_Click(object sender, EventArgs e)
{
String arg1 = "\"" + lblFilename.Text.Trim() + "\"";
String arg2 = OccamsReader.Properties.Settings.Default.ILLNumber;
//txtFilename.Text = Directory.GetCurrentDirectory() + "\\occamwork\\" + arg2 + ".zip";
txtFilename.Text = "C:\\occamsreader\\occamwork\\" + arg2 + ".zip";
if (radioDefault.Checked) { // Use Default Settings
String arg0 = "default";
System.Diagnostics.Process prc = new System.Diagnostics.Process();
//prc.StartInfo.FileName = "C:\\Temp\\occam.bat";
prc.StartInfo.FileName = "C:\\occamsreader\\occam.bat";
prc.StartInfo.Arguments = String.Format("{0} {1} {2}", arg0, arg1, arg2);
prc.Start();
} else { // Use Custom Settings
String arg5 = ""; // Quality
String arg6 = ""; // DPI
String arg7 = ""; // Grayscale
/*
switch (comboQuality.SelectedIndex)
{
case 0: // Low
arg5 = "-quality 75";
break;
case 1: // Medium
arg5 = "-quality 20";
break;
case 2: // High
arg5 = "-quality 1";
break;
case 3: // Default
arg5 = "";
break;
}
*/
switch (comboDPI.SelectedIndex) {
case 0:
//arg6 = "-resample 72x72";
arg6 = "-density 216";
break;
case 1:
//arg6 = "-resample 96x96";
arg6 = "-density 96";
break;
case 2:
//arg6 = "-resample 150x150";
arg6 = "-density 144";
break;
case 3:
//arg6 = "-resample 300x300";
arg6 = "-density 288";
break;
case 4: // Default
arg6 = "";
break;
}
if (chkGrayscale.Checked) {
arg7 = "-grayscale Rec709Luma";
}
String arg0 = "custom";
System.Diagnostics.Process prc = new System.Diagnostics.Process();
//MessageBox.Show(arg0 + arg1 + arg2 + arg5 + arg6 + arg7);
//prc.StartInfo.FileName = "C:\\Temp\\occam.bat";
prc.StartInfo.FileName = "C:\\occamsreader\\occam.bat";
prc.StartInfo.Arguments = String.Format("{0} {1} {2} {3} {4} {5}", arg0, arg1, arg2, arg5, arg6, arg7);
prc.Start();
}
}
private void btnClipboard_Click(object sender, EventArgs e)
{
Clipboard.Clear();
Clipboard.SetText(txtFilename.Text);
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
////comboQuality.Visible = true;
////comboQuality.Enabled = true;
comboDPI.Visible = true;
comboDPI.Enabled = true;
//comboFontName.Visible = true;
//comboFontName.Enabled = true;
//comboFontSize.Visible = true;
//comboFontSize.Enabled = true;
////lblQuality.Visible = true;
lblResample.Visible = true;
//lblNote.Visible = true;
//lblFontName.Visible = true;
//lblFontSize.Visible = true;
chkGrayscale.Visible = true;
chkGrayscale.Enabled = true;
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
comboQuality.Visible = false;
comboQuality.Enabled = false;
comboDPI.Visible = false;
comboDPI.Enabled = false;
//comboFontName.Visible = false;
//comboFontName.Enabled = false;
//comboFontSize.Visible = false;
//comboFontSize.Enabled = false;
lblQuality.Visible = false;
lblResample.Visible = false;
//lblNote.Visible = false;
//lblFontName.Visible = false;
//lblFontSize.Visible = false;
chkGrayscale.Visible = false;
chkGrayscale.Enabled = false;
}
}
}
Screenshots and further explanation are coming soon.
ILLiad add-on
Inside the Addons folder in your ILLiad installation, create a folder called OccamsReader. In this folder there will be two files: config.xml and OccamsReader.lua.
Here is the content for config.xml:
<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Occam's Reader</Name>
<Author>Occam's Reader Project</Author>
<Version>1.1</Version>
<Active>True</Active>
<Type>Addon</Type>
<Description>Provides a method for lending and borrowing eBooks between institutions. Learn more at OccamsReader.org</Description>
<Forms>
<Form>FormRequest</Form>
</Forms>
<Settings>
<Setting name="RouteQueue" value="Awaiting OCLC Sending" type="string">
<Description>The queue to route an OccamsReader borrowing request. Should be Awaiting OCLC Sending.</Description>
</Setting>
<Setting name="AutoRouteBorrowing" value="true" type="boolean">
<Description>Toggles whether borrowing requests are automatically routed to the RouteQueue when the Borrowing Import button is clicked.</Description>
</Setting>
<Setting name="YourOCLCSymbol" value="ZZZ" type="string">
<Description>Please add your OCLC symbol here.</Description>
</Setting>
</Settings>
<Files>
<File>OccamsReader.lua</File>
</Files>
</Configuration>
And here is the content for OccamsReader.lua:
-- Occam's Reader Add-on
-- Version 1.1
-- October 10, 2014
-- Contact: libraries.occams.reader@ttu.edu
-- Initialize local variables for our interface:
local interfaceMngr = nil;
local occamsReader = {};
occamsReader.Form = nil;
occamsReader.RibbonPage = nil;
occamsReader.Browser = nil;
occamsReader.ImportButton = nil;
local settings = {}
settings.RouteQueue = GetSetting("RouteQueue");
settings.AutoRouteBorrowing = GetSetting("AutoRouteBorrowing");
settings.YourOCLCSymbol = GetSetting("YourOCLCSymbol");
require "Atlas.AtlasHelpers";
function Init()
-- Configure the add-on for either Lending or Borrowing:
if GetFieldValue("Transaction", "ProcessType") == "Lending" then
-- We show the Lending page:
interfaceMngr = GetInterfaceManager();
occamsReader.Form = interfaceMngr:CreateForm("Occam's Reader", "Script");
occamsReader.Browser = occamsReader.Form:CreateBrowser("Occam's Reader", "Occam's Reader Browser", "Occam's Reader");
occamsReader.Browser.TextVisible = false;
occamsReader.RibbonPage = occamsReader.Form:GetRibbonPage("Occam's Reader");
occamsReader.RibbonPage:CreateButton("Lending Import", GetClientImage("ImportData32"), "Import_Lender", "Occam's Reader");
occamsReader.Form:Show();
HomeLending();
else
-- We show the Borrowing page:
interfaceMngr = GetInterfaceManager();
occamsReader.Form = interfaceMngr:CreateForm("Occam's Reader", "Script");
occamsReader.Browser = occamsReader.Form:CreateBrowser("Occam's Reader", "Occam's Reader Browser", "Occam's Reader");
occamsReader.Browser.TextVisible = false;
occamsReader.RibbonPage = occamsReader.Form:GetRibbonPage("Occam's Reader");
occamsReader.RibbonPage:CreateButton("Borrowing Import", GetClientImage("ImportData32"), "Import_Borrower", "Occam's Reader");
occamsReader.Form:Show();
HomeBorrowing();
end
end
function HomeLending()
if GetFieldValue("Transaction", "SystemID") == "RAPID" then
-- do not process email address because it will have junk in it:
occamsReader.Browser:Navigate("http://occamsreader.org/lending.php?illn="..GetFieldValue("Transaction", "ILLnumber").."&instemail=UNKNOWN_EMAIL_ADDRESS")
else
-- Pass the ILL number and Borrowing Institution Email to the Lending webpage normally:
borrowingEmail = borrowerEmail()
occamsReader.Browser:Navigate("http://occamsreader.org/lending.php?illn="..GetFieldValue("Transaction", "ILLnumber").."&instemail="..AtlasHelpers.UrlEncode(borrowingEmail))
end
end
function HomeBorrowing()
-- Pass the ILL number and Title to the Borrowing webpage:
occamsReader.Browser:Navigate("http://occamsreader.org/discover.php?isbn="..GetFieldValue("Transaction", "ISSN").."&title="..AtlasHelpers.UrlEncode(GetFieldValue("Transaction", "LoanTitle")).."&exclude="..settings.YourOCLCSymbol)
end
function Import_Lender()
-- Note that C:\occamsreader must exist with open permissions
-- Launch the Occam's Reader image conversion software:
os.execute("C:\\occamsreader\\OccamsReader.exe "..GetFieldValue("Transaction", "ILLnumber"))
end
function Import_Borrower()
-- Import the Lending String and URL into the request:
--interfaceMngr:ShowMessage("Import_Borrower()", " ");
local lendingStringResults = occamsReader.Browser:GetElementInFrame(nil, "lendingStringResults");
if lendingStringResults == nil then
-- do nothing
else
lsr = lendingStringResults:GetAttribute("title");
SetFieldValue("Transaction", "LendingString", lsr);
end
local titleURLResults = occamsReader.Browser:GetElementInFrame(nil, "titleURLResults");
if titleURLResults == nil then
-- do nothing
else
tur = titleURLResults:GetAttribute("title");
SetFieldValue("Transaction", "Location", tur);
end
SetFieldValue("Transaction", "CallNumber", "OccamsReader");
ExecuteCommand("SwitchTab", {"Detail"});
--interfaceMngr:ShowMessage("Import_Borrower() 2", " ");
--if {settings.AutoRouteBorrowing, false} == true then
if settings.AutoRouteBorrowing == true then
-- auto route the borrowing request:
--interfaceMngr:ShowMessage("Auto Route Borrowing Request", " ");
ExecuteCommand("Save", {});
ExecuteCommand("Route", {settings.RouteQueue, false});
else
-- do not auto route the borrowing request
-- (this allows for review of the lending string, etc.)
--interfaceMngr:ShowMessage("DO NOT Auto Route Borrowing Request", " ");
end
end
function borrowerEmail()
-- We assume that we are in a lending situation.
-- Retrieve the Borrowing Institution email address from the database:
local connection = CreateManagedDatabaseConnection();
connection.QueryString = "SELECT BorrowingDeptEMail FROM LenderAddresses where LenderString = '" .. GetFieldValue("Transaction", "LendingLibrary") .. "'";
connection:Connect();
local DeptEmail = connection:ExecuteScalar();
connection:Disconnect();
--interfaceMngr:ShowMessage("The borrower email address is " .. DeptEmail, "Email Information");
return DeptEmail;
end
Web pages within the ILLiad add-on
The discovery layer was the starting point for borrowing during the Occam's Reader Pilot. It was developed in conjunction with Springer. The first page in the discovery process is discover.php, which is shown below:
<html><head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<title>Occam's Reader Discovery Tool</title>
<link type="text/css" media="all" rel="stylesheet" href="css/style.css" />
<link type="text/css" href="css/custom-theme/jquery-ui-1.8.21.custom.css" rel="stylesheet" />
</head>
<body>
<div id="content">
<?php
//echo $_REQUEST['homeinst'];
?>
<img src="occams_reader.png" alt="Occam's Reader" width="50%" style="margin: 15px 0 15px 0;" /><br />
<h1>Discovery Tool</h1>
<p><strong>Search 964,655 Springer eBook items available for interlibrary loan among the 33 GWLA member institutions.</strong></p>
<p>For best results, search by ISxN. Search by title will accept partial titles, e.g. <em>Gone</em> instead of <em>Gone with the Wind.</em></p>
<p style="font-weight:bold;color:#ff3333;"> </p>
<!--p style="font-weight:bold;color:#ff3333;">12/16/2014: We are currently experiencing problems lending eBooks with Occam's Reader.</p-->
<p>
<table border="0" cellspacing="5" cellpadding="0">
<tr>
<form action="finalresults.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="exclude" id="exclude" value="<?php echo isset($_REQUEST['exclude']) ? $_REQUEST['exclude'] : '';?>" />
<td align="right">ISBN/ISSN: </td><td><input value="<?php echo isset($_REQUEST['isbn']) ? $_REQUEST['isbn'] : '';?>" type="text" name="item_isxn" size="30" /> <input type="submit" name="submit" value="Search ISxN" /></td>
</form>
</tr>
<tr>
<form action="intermediateresults.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="exclude" id="exclude" value="<?php echo isset($_REQUEST['exclude']) ? $_REQUEST['exclude'] : '';?>" />
<td align="right">TITLE: </td><td><input value="<?php echo isset($_REQUEST['title']) ? $_REQUEST['title'] : '';?>" type="text" name="item_title" size="30" /> <input type="submit" name="submit" value="Search Title" /> (Partial titles are OK.)</td>
</form>
</tr>
<tr><td></td><td></td></tr>
</table>
</p>
<p> </p>
<p><strong>Important Copyright Information</strong><br />
The copyright law of the United States (Title 17, United States Code) governs the making of photocopies or other reproductions of copyrighted materials. Under certain conditions specified in the law, libraries and archives are authorized to furnish a photocopy or other reproduction. One of these specified conditions is that the photocopy or reproduction is not to be "used for any purpose other than private study, scholarship, or research". If a user makes a request for, or later uses, a photocopy or reproduction for purposes in excess of "fair use," that user may be liable for copyright infringement. </p>
<p> </p>
<div class="ui-state-highlight ui-corner-all" style="margin: 20px 0 20px 0; padding: 0 .7em;text-align:left;">
<p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
<strong>Occam's Reader</strong> provides a single, secure method for lending libraries to share eBooks and similar resources. Contact <a href="mailto:libraries.occams.reader@ttu.edu">libraries.occams.reader@ttu.edu</a> for more information.</p>
</div>
<p> </p>
</div>
</body></html>
More pages and explanation are coming soon.
Web viewer for patrons
The main file for the patron viewing experience is reader.php. Here it is:
<?php
$PATH_TO_BOOK = $_REQUEST['illn'];
if (chdir($PATH_TO_BOOK)) {
// fall through...
} else {
echo 'Item number ' . $PATH_TO_BOOK . ' is invalid or has expired. Please make a new request from your home institution.<br />If you require technical assistance, please contact <a href="mailto:libraries.occams.reader@ttu.edu?subject=Item%20' . $PATH_TO_BOOK . '%20invalid%20or%20expired">libraries.occams.reader@ttu.edu</a><br />';
exit();
}
$listing = `dir /b *.png`;
$files = explode("\n", $listing);
array_pop($files); // needed to get rid of last blank line
natsort($files);
$lastpage = count($files);
$tileSources = "";
foreach($files as $file){
$tileSources.= "
{
type: 'legacy-image-pyramid',
levels: [
{
url: '$PATH_TO_BOOK/$file',
height: 2000,
width: 1545
}
]
},";
}
$tileSources = rtrim($tileSources, ",");
?>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Occam's Reader Demo</title>
<link href='http://fonts.googleapis.com/css?family=Montserrat' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="css/reset.css" type="text/css" />
<link rel="stylesheet" href="css/magnific-popup.css" type="text/css" />
<link rel="stylesheet" href="css/readerstyle.css" type="text/css" />
<!--[if gte IE 6]><link rel="stylesheet" href="ie.css"><![endif]-->
<script src="js/jquery-1.11.0.min.js"></script>
<script src="js/jquery.magnific-popup.min.js"></script>
<script src="js/openseadragon.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var viewer = OpenSeadragon({
id: "contentDiv",
prefixUrl: "images/",
animationTime: 0.2,
showNavigator: false,
zoomPerClick: 1.2,
zoomInButton: "zoom-in",
zoomOutButton: "zoom-out",
preserveViewport: true,
tileSources: [<?php echo $tileSources;?>]
});
// remove default buttons
// also need navImages setting above
viewer.clearControls();
function goToNextPage() {
var pageNo = parseInt($("#pageNo").val());
if(pageNo > <?php echo $lastpage - 1; ?>) return; /* CHECK HERE */
document.getElementById('pagetext').innerHTML = '<input id="pageNo" type="text" value="' + ( pageNo + 1 ) + '"> of <?php echo $lastpage; ?>';
viewer.goToPage(pageNo); /* was +1 */
}
function goToPrevPage() {
var pageNo = parseInt($("#pageNo").val());
if(pageNo <= 1) return;
document.getElementById('pagetext').innerHTML = '<input id="pageNo" type="text" value="' + ( pageNo - 1 ) + '"> of <?php echo $lastpage; ?>';
viewer.goToPage(pageNo - 2); /* was -2 */
// subtract 2 because page 1 is page 0
}
// Page navigation - arrow links
$("#next").click(function() { goToNextPage(); });
$("#previous").click(function() { goToPrevPage(); });
// Page navigation - left/right arrow keys
$(document).keypress(function(e) {
var code = e.keyCode || e.which;
if (code == '37') { goToPrevPage(); }
else if (e.keyCode == '39') { goToNextPage(); }
});
// Jump to page - green arrow
$("#pageNoSubmit").click(function(){
var pageNo = parseInt($("#pageNo").val()) - 1;
viewer.goToPage(pageNo);
});
// Jump to page - enter key in input box
$(document).keypress(function(e){
$('#pageNo').keypress(function(e) {
var code = e.keyCode || e.which;
if (code == 13) {
var pageNo = parseInt($("#pageNo").val()) - 1;
viewer.goToPage(pageNo);
}
});
});
// Rotate right control
$("#rotateright").click(function() {
var curval = viewer.canvas.style["transform"] || "rotate(0deg)";
var matches = curval.match(/\((\d+)deg/);
if (matches) {
var curangle = parseInt(matches[1]);
curangle += 90;
curangle %= 360;
viewer.canvas.style["transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-ms-transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-moz-transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-webkit-transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-o-transform"] = "rotate("+curangle+"deg)";
}
});
// Rotate left control
$("#rotateleft").click(function() {
var curval = viewer.canvas.style["transform"] || "rotate(0deg)";
var matches = curval.match(/\((\d+)deg/);
if (matches) {
var curangle = parseInt(matches[1]);
curangle -= 90;
if (curangle < 0) curangle += 360;
viewer.canvas.style["transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-ms-transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-moz-transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-webkit-transform"] = "rotate("+curangle+"deg)";
viewer.canvas.style["-o-transform"] = "rotate("+curangle+"deg)";
}
});
// Feedback Button
$('.popup-with-form').magnificPopup({
type: 'inline',
preloader: false,
focus: '#name',
// When elemened is focused, some mobile browsers in some cases zoom in
// It looks not nice, so we disable it:
callbacks: {
beforeOpen: function() {
if($(window).width() < 700) {
this.st.focus = false;
} else {
this.st.focus = '#name';
}
}
}
});
$("#send-feedback").on("submit", function(e) {
e.preventDefault();
var results = $.post( "submit.php", $( "#send-feedback" ).serialize() );
console.log(results);
$("#feedback-form-stage1").slideUp(500, function() {
$("#feedback-form-stage2").slideDown(500);
});
});
});
</script>
</head>
<body>
<div class="left-controls">
<ul>
<li><a href="javascript:;" class="rotateright" id="rotateright"></a></li>
<li><a href="javascript:;" class="zoomin" id="zoom-in"></a></li>
<li><a href="javascript:;" class="zoomout" id="zoom-out"></a></li>
<li><a href="javascript:;" class="rotateleft" id="rotateleft"></a></li>
</ul>
</div>
<div id="page">
<a id="previous" class="leftarrow" href="javascript:;" ></a>
<div id="contentDiv" class="openseadragon"></div>
<a id="next" class="rightarrow" href="javascript:;" ></a>
</div>
<div id="pagecontrol">
<span id="pagetext"> <input id="pageNo" type="text" value="1" > of <?php echo $lastpage; ?> </span>
<span type="submit" href="" id="pageNoSubmit" /></span>
</div>
<a class="popup-with-form" href="#feedback-form"><div id="feedback-dock" >
<!-- <a id="feedback-btn" href="javascript:;" >Feedback [ + ]</a> -->
<!-- link that opens popup -->
</div></a>
<!-- form itself -->
<div id="feedback-form" class="white-popup mfp-hide">
<iframe src="https://docs.google.com/forms/d/1LVcFANcNeo2T18sfS8uiIRF7c6xmm8nVKowsxIPwqyQ/viewform?embedded=true" width="100%" height="80%" frameborder="0" marginheight="0" marginwidth="0">Loading...</iframe>
</div>
</body>
</html>
More web viewer explanation and content coming soon.