Making a Prompter – part 2


This post continues from part one. Term finished, and I had a little bit of headroom to start thinking about my coding again. I tried to polish my code, but couldn’t solve all of its problems. I tried a different approach, using XSLT to change how a TEI-XML version of a play was displayed, such that it showed only a single part. I couldn’t get this to work either, although I am certain that this method would probably have been the best way of doing things. As it was, the one thing I did take away from my fiddling around with XSLT and TEI was the very impressive TEI Boilerplate, which converts TEI-XML into HTML, and so to a version that renders nicely in a web browser.

After a lot of frustrating attempts to learn new ways of doing things, I gave up and returned to the same language that I had used for my earlier project: javascript. I downloaded a HTML version of a play from the Oxford Text Archive, and wrote a script that – at a click of a button – did everything that I had wanted my efforts in python to achieve. There’s a lot to be said for working in a way that is (or, rather, once was) familair to you, and I found myself looking at old code and building upon techniques I had acquired before my PhD. Of course, it wasn’t all smooth sailing. Although I overcame the difficulties of lines without cues, and some encoding errors, I also quicly realised that many plays did not have consistent speech headings for their characters. Solving this one required a bit of user input, so I wrote an interface that allowed for the selection of multiple character names in order to gather everything spoken by a single character, whether he was known as clown or Feste, etc.

The core workings of the programme didn’t really change: it imported a playtext, it located all its speeches, and for each speech spoken by the chosen character (regardless of his or her current name), it generated the cue before printing the part. There were now two major things to work on: the time it took the programme to do all of this, and – more pressing – the fact that this programme only worked on one text.

Solving the latter issue required me to learn a little bit of AJAX, which – paired with some PHP – gave users the possibility of uploading texts in TEI-XML to the prompter. I also put some ready-made texts on the server for testing purposes. As for the question of time, this ultimately led to a slight redesign in the programme. The key issue was that, in order to work out whether a line was the first in an act (and so did not need a cue), my programme was examining the place of every speech in the document. This took ages. Although it would limit the programme’s capacity in the future, I decided to reorganize the way my javascript worked. It would now – as before – import a text, but would then use that text to generate its own, very simple version, divided into acts and containing speeches and speech headings but precious little else. This simplified document could be processed much faster, although I still put up a little ‘Loading…’ screen for those whose computers would struggle under the load.

I was pretty happy with all of this. All that remained now was beautifying the website and clearing up some corner cases (what if a line had a very short cue? what it if you wanted to print the part? what if you wanted one, two or four cue words? what if you wanted to generate another character’s part after running the programme once? what if you wanted to check the part against the text of the full play?). It was now midsummer, though, and I was going on holiday.

Here follows, by way of conclusion, a glimpse at the javascript (including AJAX and jquery – the two things I picked up thanks to this project, having refused to learn them previously). You might spot the vast number of console.log() here, which remain as testament to the amount of bug-hunting I ended up doing for this function, whose job is to prepare the simplified version of the user’s chosen playtext.

function partMaker(uploadFileName){
//Hide the <div id="results">
var resultsDiv = document.getElementById('desk2');
resultsDiv.style.display="none";
//Obtain the XML of the play
var Connect = new XMLHttpRequest();
Connect.open("GET", uploadFileName, false);
Connect.setRequestHeader('Content-Type','text/xml');
Connect.send();
TheXML = Connect.responseXML; //note that this is a global variable console.log(TheXML);
//Count number of acts
var collectionofActs = TheXML.querySelectorAll('[type="act"]');
//console.log(collectionofActs);
var numberofActs = collectionofActs.length;
//console.log(numberofActs);
//Append an act div for each act to the 'part'
var i;
for (i = 0; i <numberofActs; i++){
var anAct = document.createElement('Div');
var actNumber = i+1;
anAct.id ='act '+ actNumber; document.getElementById('part').appendChild(anAct); }
//Pull out all the lines (<sp> tags) from the XML
var allSP = TheXML.getElementsByTagName("sp");
var i;
var numberOfSP = allSP.length;
//Loop through each <sp> of the xml to generate the script for the part
for (i = 0; i < numberOfSP; i++) {
var theSP = allSP[i];
//console.log(theSP);
//console.log(theSP.children);
//console.log(theSP.children.length);
//find the act of the <sp>
var parentAct = $( theSP ).closest('[type~="act"]');
//console.log(parentAct);
var nextSiblingsOfAct = $( parentAct ).nextAll('div');
//console.log(nextSiblingsOfAct);
var numberofActSiblings = nextSiblingsOfAct.length;
//console.log(numberofActSiblings);
var thisActNumber = numberofActs - numberofActSiblings;
//Place the <div class="sp"> in the correct act's <div> ready to receive additional elements from XML's <sp>
var theSPDiv = document.createElement("div");
theSPDiv.className = "sp";
document.getElementById("act "+thisActNumber).appendChild(theSPDiv);
//Subloop through each element in XML's <sp>
for (j=0;j<theSP.children.length;j++){
//Series of ifs to cover all elements of a <sp> in TEI, appending each as HTML to my <div id="part">
if (theSP.children[j].tagName === 'speaker'){
//Get the text of <speaker>
var theSpeaker = theSP.children[j].textContent; theSpeaker = theSpeaker.trim();
//console.log(theSpeaker);
//create a <div class="speaker">
var theSpeakerDiv = document.createElement("div");
theSpeakerDiv.className = "speaker";
//create a text node and put the text of <speaker> from the XML in it
var theSpeakerTextNode = document.createTextNode(theSpeaker);
//put the text node into the <div class="speaker">
theSpeakerDiv.appendChild(theSpeakerTextNode);
theSPDiv.appendChild(theSpeakerDiv); }
if (theSP.children[j].tagName === 'p'){
//Get the text of <p>
var theP = theSP.children[j].textContent;
//console.log(theP);
//create a <div class="speech">
var thePDiv = document.createElement("div");
thePDiv.className = "speech";
//create a text node from the value of the XML's <p> and put it into the <div class="speech">
var thePTextNode = document.createTextNode(theP);
thePDiv.appendChild(thePTextNode);
theSPDiv.appendChild(thePDiv); }
if (theSP.children[j].tagName === 'l'){
//Get the text of <l>
var theL = theSP.children[j].textContent;
//console.log(theL);
//create a <div class="verse-line">
var theLDiv = document.createElement("div");
theLDiv.className = "verse-line";
//create a text node from the value of the XML's <p> and put it into the <div class="speech">
var theLTextNode = document.createTextNode(theL);
theLDiv.appendChild(theLTextNode);
theSPDiv.appendChild(theLDiv); }
}//end of subloop for <sp>'s possible elements
}//end of loop through all <sp> in source XML: part <div> is now complete
}


One response to “Making a Prompter – part 2”