# XScript.js

## XScript

The object representing the transcriber as a whole is XScript. It is initialized with values clip0 and clipsPerPage.

The page structure is:

- Media player, represented by a Media object.
- The navigation frame, represented by a NavigationFrame object. It contains:
  - a NavCell that allows direct access to nearby pages
  - Up-triangle and down-triangle buttons that go to the immediately preceding and following pages.
- The clip table, which is embedded in the navigation frame. It is represented by a ClipTable object.
  - Each row of the table displays one clip, and is represented by a Clip object.
- The TextEditBox, a floating element that is swapped in for a snippet-text paragraph when it is being actively edited.

The members are:

- clip0 - the index of the first clip displayed.
- clipsPerPage - the number of clips displayed in a full page.
- server - a Server instance, to handle calls to the web server.
- media - a Media instance.
- nav - a NavigationFrame instance.
- table - a ClipTable instance.
- editBox - a TextEditBox instance.

new XScript(clip0, clipsPerPage)
  Constructor. Creates all the elements. Calls _setDuration to the current media duration.
_setDuration(duration)
  Screens out cases in which the XScript is not yet initialized, or duration is not a genuine value. Otherwise, passes the duration to table.setDuration.

## Media

The Media object manages the media player. Member:

- clip - set by playClip()

new Media(xscript)

  The xscript is the central controller. When the media file is loaded, duration-change events are generated; they are passed on to the xscript's _setDuration method.

  When the media file is playing, time-update events are generated. If _stopAt is set and is exceeded, playback is halted, _startAt and _stopAt are cleared, and, if there is a clip, it receives an atEnd() call.

time()
  Returns the time in seconds of the current playback position.
duration()
  Returns the total length in seconds of the media file.
startPlaying(clip)
  Starts playing at the beginning of the clip. Will continue until the user stops it or it reaches the end of the media.
stop()
  Stops playback.
playClip(clip)
  Sets _startAt and _stopAt and starts playback.
playSpan(start, end)
  Implements playClip. Sets _startAt and _stopAt, moves the playback head to _startAt, and starts playback. Playback will be halted when the time exceeds _stopAt.

## NavigationFrame

The NavigationFrame consists of the NavCell, which lets the user jump directly to nearby pages, and the up-triangle and down-triangle buttons, which jump to the immediately previous and immediately following pages.

new NavigationFrame(xscript)
  Constructor; just stores xscript as a member.
update(start, end, totalNClips)
  Regenerate the navigation frame. It calls NavCell.update to regenerate the NavCell. It sets the URLs for the up-triangle and down-triangle buttons to 'edit.x', where x is start -/+ clipsPerPage. (If the current page is first or last, the buttons will be disabled instead.)

## NavCell

NavCell is a variable. The NavCell is the bar between the media controls and the snippet table that allows one to jump to other pages. The value of NavCell is an object.

elt
  The navCell element.
update(start, clipsPerPage, totalNClips)
  Regenerate the contents of the element. Delete all the existing children. The value for start is the index of the starting clip. Do integer division by clipsPerPage to turn that into a page number, which is the current page. Add buttons for the 5 pages before and after the current page; fewer if fewer are available.
button(i)
  Add a Button linked to the i-th page of clips. The URL is 'edit.n' where n is a clip number, obtained by multiplying i times the clipsPerPage.
ellipsis()
  Add an ellipsis; used if there are more than 5 pages before or after the current page.
addCurrent(i)
  Add a Button for the current page, having index i. The current page is special because it is not a live link, and it has angle brackets around it.

## ClipTable

The ClipTable contains one row per clip. Its members are:

- xscript - the XScript object.
- element - the clipTable web page element.
- clips - a list of Clip instances.
- start - the index of the first Clip.
- end - the index of the last Clip plus one.
- totalNClips - in the entire transcription, not just this page.

new ClipTable(xscript)
  Constructor. Clips, start, end, and totalNClips are initially null.
getClip(index)
  Returns the Clip whose index is index, or null if there is no such Clip. Note that the index is displayed in the leftmost column; it is the index of the Clip in the clips file, not its position in the table's clips member. The table only contains clips belonging to the current pageful.
setDuration(duration)
  Calls server.setDuration. This is a hack: I do not have code for parsing the contents of MP3 and MP4 files, so I am using the web browser's player to tell me what the duration is.
_rebuild(spec)
  This is a callback from the server. It is triggered when a call is issued that may change the number of clips, namely server.set_duration or server.set_clip. The spec is a JSON list with one element per clip. Each element has attributes: i, start, end, ascii, unicode. All elements of the clip table are deleted and recreated. Repeated new Clip calls are placed, and finally nav.update is called.

## Clip

A Clip represents one row of the ClipTable. It has members:

- xscript - the XScript object.
- index - the index of this clip.
- ascii - the romanized text for this clip.
- start - the start position for this clip, in seconds.
- end - the end position for this clip, in seconds.
- para - the new-paragraph flag for this clip, 0 or 1.
- pElt - the 'p' element containing the text of the clip.
- _playControls - a PlayControls instance.
- _boundaryControls - a BoundaryControls instance.

new Clip(table, spec)
  Not only initializes the clip but also appends it to the table. The spec is a JSON object with attributes i, start, end, ascii, unicode. A new row is created in the web page table. It contains five cells:
  - The clip index.
  - The play controls, an instance of PlayControls. It calls methods play() and cut().
  - The boundary controls, an instance of BoundaryControls. It calls methods playHead(), playTail(), and merge().
  - The new-paragraph checkbox, which calls clip.setPara(value) when toggled.
  - The text paragraph, which displays the unicode text of the clip. It calls clip.editText() when clicked.
play()
  Calls xscript.media.playClip(this). Also calls _boundaryControls.selectLeftBoundary().
playHead()
  Plays the first 3/4 second of the clip.
playTail()
  Plays just the last 3/4 sec of the clip. Calls xscript.media.playSpan.
atEnd()
  This method is called by the media player when clip playback completes. Passes a stopped() notification to the play controls and selectRightBoundary() to the boundary controls.
cut(t)
  Calls server.cut(this, t).
_nudged(boundary, actual)
  When server.nudge receives a response, it calls this method, which sets the appropriate start/end position of this clip and the adjacent one, and passes a _nudged(boundary) notification to the boundary controls. Boundary is either 'left' or 'right'.
prevClip()
  Returns the previous Clip, or null if the previous clip does not exist or is not displayed.
nextClip()
  Returns the next Clip, or null if the next clip does not exist or is not displayed.
merge(boundary)
  This is called by the boundary-delete button in the boundary controls. It calls server.merge(index), where index is this Clip's index, if boundary is 'right', and one less, if boundary is 'left'.
setPara(value)
  Sets the new-paragraph flag: 0 or 1. Calls server.setPara(index, value).
editText()
  Calls editBox.editClip(this). The edit box calls setText(text) unless editing is canceled.
setText(text)
  Sets ascii and calls server.setText(this).
_updateUnicode(unicode)
  When server.setText receives a response, it calls this method to display the unicode version of the new text.

## PlayControls

The play controls include the play button and the cut button. The play button calls togglePlay() when clicked, and the cut button calls cut(). Members:

- xscript - the XScript.
- clip - the Clip.
- _playing - whether the play button has been clicked.

togglePlay()
  If _playing, call stop(), otherwise call start().
start()
  Calls clip.play(). Changes the play button symbol to the stop symbol, activates the cut button.
stop()
  Directly stops the media player. Changes the play button symbol to the play symbol, deactivates the cut button.
stopped()
  Called by stop() to change the play button symbol and deactivate the cut button. Also called by the media player when playback reaches the end of the clip.
cut()
  If _playing, stop playback and call clip.cut(t), where t is 1/2 sec before the cut button was clicked.

## BoundaryControls

Each Clip has its own BoundaryControls object. This contains the left and right boundary selection buttons, the nudge buttons, and the boundary-delete button. Members:

- clip - the Clip
- boundary - either 'left' or 'right', depending on which boundary has been selected

new BoundaryControls(cell, clip)
  Constructor. Creates all the buttons. When one of the selection buttons is clicked, either selectPlayLeftBoundary() or selectPlayRightBoundary() is called. When one of the nudge buttons is clicked, nudge(dir) is called. When the delete button is clicked, merge() is called.
selectPlayLeftBoundary()
  Calls selectLeftBoundary() and clip.playHead().
selectPlayRightBoundary()
  Calls selectRightBoundary() and clip.playTail().
selectLeftBoundary()
  Highlights the left boundary button and activates the nudge and delete buttons.
selectRightBoundary()
  Highlights the right boundary button and activates the nudge and delete buttons.
setDisabled(value)
  Does the actual activation or deactivation of the nudge and delete buttons.
nudge(dir)
  Disables the buttons and calls server.nudge(clip, boundary, delta). The boundary is either 'left' or 'right' and the delta is either -0.25 or +0.25, depending on dir. When the server receives a response, it will call clip._nudged(), which calls this control's _nudged() method.
_nudged(boundary)
  Callback. Calls either selectPlayLeftBoundary() or selectPlayRightBoundary(), depending on boundary.
merge()
  Calls clip.merge(boundary).

## TextEditBox

The TextEditBox swaps itself in for the clip text pElt when the clip is being edited. It has two members:

- box - the HTML text input box.
- clip - the Clip that is being edited, initially null.

new TextEditBox(xscript)
  Constructor. Two event handlers are set up: _keypress receives all key presses, and cancelEdit is called if the user clicks away from the box.
editClip(clip)
  Called by Clip.editText. If a (different) clip is currently being edited, cancelEdit is called. The contents of the box is set to the contents of clip.ascii. The clip's pElt is replaced with the box, and focus is directed to the box.
_keypress(evt)
  The enter key calls finishEdit. The escape key calls cancelEdit.
cancelEdit()
  Put the clip's pElt back in place of the box.
finishEdit()
  Get the ascii contents of the text box. Call cancelEdit to hide the text box. Call clip.setText(ascii).

## Server

The Server object manages Ajax calls to the web server. The calls are received by a Transcriber object on the server side. Member:

- xscript - the XScript object.

new Server(xscript)
  Constructor.
setDuration(duration)
  Calls Transcriber.set_duration(clip0, clipsPerPage, duration). The clip0 and clipsPerPage values are taken from xscript. The response from the server is a JSON spec for the updated clips, which is passed to xscript.table._rebuild.
cut(clip, t)
  Makes an ajax call to Transcriber.cut. Passes the response to xscript.table._rebuild.
nudge(clip, boundary, delta)
  Makes an ajax call to Transcriber.nudge. Passes the response to clip._nudged.
merge(index)
  Makes an ajax call to Transcriber.merge. Passes the response to xscript.table._rebuild.
setPara(index, value)
  Calls Transcriber.set_para(index, value). No response from server.
setText(clip)
  Calls Transcriber.set_text(index, text). The response from the server is Unicode text, which is passed to clip._updateUnicode.

## Utility

removeChildren(elt)
  Deletes all children of element elt.