Shlexer
A basic XML parser for doing token analysis on uploaded scripts.
by tambascot
HTML
<html>
<? $VERSION = "0.1"; ?>
<head>
<title>Shlexer - A Shakespeare Lexer</title>
<!-- CSS goes here -->
<style>
</style>
<!-- Javascript goes here -->
<script>
</script>
</head>
<body>
<h1>
Shlexer
</h1>
<h3>
A <u>Sh</u>akespeare <u>lexer</u>.
</h3>
<p>
Shlexer extracts the words from a play text, counts each occurence of that word, and then displays a table with that information. You can also limit the scope of the lexing by either a character, an act, a scene, or some combination of all of the above.
</p>
<p>
"Why do this," you ask? Some actors and directors will take a high frequency of a certain word as an indicator that it has some special meaning in a play, or a characters' use of that word as a guide to understanding that character. Counting that sort of info is something computers should be good at.
</p>
<p>
This is a JavaScript implementation of a program I initially wrote in Ruby when I started grad school in 2009, which was then designed to work with bare text files.
</p>
<p>
This is currently only designed to work with the TEI Simple encoded XML texts published by the Folger Shakespeare Library. You can download those from <a href="https://www.folger.edu/explore/shakespeares-works/download/"> https://www.folger.edu/explore/shakespeares-works/download/</a>
</p>
<input type="file" onchange="readText(event)" />
<pre id="output"></pre>
<div id="selection_lists">
<!-- Selection lists to control which characters
and acts / scenes we examine -->
<div id="cast_list">
<!-- to be filled in by javascript -->
</div>
<div id="scene_list">
<!-- to be filled in by javascript -->
</div>
<div id="countWords_button">
<!-- to be filled in by javascript -->
</div>
</div>
<div id="results">
<!-- The results of the analysis...
CSS
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
td {
padding: 1px;
}
#footer {
font-size: small;
}
#changelog {
display: none;
}
JavaScript
// a global hash of words that we encounter in the script that we're analyzing. This is necessary so that we can make the display of the words and their tallys separate from other aspects of the document.
let words_hash = new Map();
// a global has of the parsed xmlDoc. This is necessary so that we do not have to fire the countWords function from within one of the functions that sets up the scene and character selection menus.
var xmlDoc;
// readText(event)
// Reads a file chosen by user into memory
// adapted from https://stackoverflow.com/questions/750032/reading-file-contents-on-the-client-side-in-javascript-in-various-browsers
async function readText(event) {
const file = event.target.files.item(0)
const text = await file.text();
parseXML(text);
}
// parseXML
// Parses XML to do... stuff...?
// INPUT: the XML text file read into a variable
// adapted from: https://www.w3schools.com/xml/xml_parser.asp
function parseXML(XMLText) {
let parser;
parser = new DOMParser();
xmlDoc = parser.parseFromString(XMLText, "text/xml");
document.getElementById("output").innerHTML =
xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue;
createCastSelect();
createSceneSelect();
document.getElementById("countWords_button").innerHTML
= "<button onclick=\"countWords_wrapper()\">Count Words</button>\n";
}
// createCastSelect
// Extracts the cast list to display as a selection menu
// INPUT: none
// RETURN: none
function createCastSelect() {
let charSelectList = "<select name='characters' id='characters'>\n <option value='All'>All</option>\n";
let castList = xmlDoc.getElementsByTagName("role");
for (let i = 0; i < castList.length; i++) {
let name = castList[i].getElementsByTagName('name')[0].textContent;
charSelectList += "<option value='" + name
+ "'>" + name + "</option>\n";
}
charSelectList += "</select>\n";
document.getElementById("cast_list").innerHTML = charSelectList;
}
//...