Books (optional): Books.html

<!DOCTYPE html>
<html>
  <head>
<!-- Based on http://web-engineering.info/JsFrontendApp by Gerd Wagner -->
<title>Javascript Example UI</title>

<style>
body {
	text-align: justify;
	font-family: arial, sans-serif;
	color: saddlebrown;
}

button {
	color: saddlebrown;
	background: beige;
	border: none;
	height: 100px;
	width: 300px;
	font-size: 14pt;
	text-align: center;
}

button.testing {
	background: cornsilk;
}

button.inpanel {
	border-radius: 20px;
	height: 50px;
	width: 120px;
}

.bigcap {
    font-size: 24pt;
}
</style>

<script>
"use strict"

class Main {
    constructor () { //*3 Attach the listeners
	this._bookDB = new BookDB()

	// Install callbacks for main panel
	createButton.addEventListener("click", () => this._createButtonCB()) //*3
	retrieveButton.addEventListener("click", () => this._retrieveButtonCB()) //*3
	updateButton.addEventListener("click", () => this._updateButtonCB()) //*3
	deleteButton.addEventListener("click", () => this._deleteButtonCB()) //*3

	// These are mainly for testing, they just do the action, no transient panel
	clearButton.addEventListener("click", () => this._clearDataCB())
	generateButton.addEventListener("click", () => this._generateTestDataCB())

	// Callbacks for widgets inside the transient panels
	createSaveButton.addEventListener("click", () => this._createSaveButtonCB()) //*3
	retrieveDoneButton.addEventListener("click", () => this._retrieveDoneButtonCB()) //*3
	updateSaveButton.addEventListener("click", () => this._updateSaveButtonCB()) //*3
	updateSelectBook.addEventListener("change", () => this._updateSelectBookCB()) //*3
	deleteDoneButton.addEventListener("click", () => this._deleteDoneButtonCB()) //*3
    }

    /*
     * Callback routines for various UI actions
     */

    /*
     * Just displays the panel, could have been a one-line anonymous function in addEventListener, 
     * but all the callbacks are here in separate functions, for uniformity/clarity 
     */
    _createButtonCB () { //*3
	this._showPanel ("createPanel"); //*3
    }

    /*
     * Called from within the panel, does the actual save operation
     */
    _createSaveButtonCB () { //*4 Called from within the panel, does the actual save operation
	this._bookDB.add (new Book (createForm.isbn.value, createForm.title.value, createForm.year.value)) //*4
	createForm.reset() //*4
	this._showPanel (null) //*4
    }

    /*
     * Displays the panel and fills it up with the data
     */
    _retrieveButtonCB () { //*5 Displays the panel and fills it up with the data
	this._showPanel ("retrievePanel") //*5

	// Clear the table display
	retrieveTable.innerHTML = ""; //*5

	// Load all book objects 
	for (let book of this._bookDB.retrieveAll()) { //*5
	    // For each book, create a table row with a cell for each of its attributes
	    let row = retrieveTable.insertRow();  //*5
	    row.insertCell(-1).textContent = book.isbn //*5
	    row.insertCell(-1).textContent = book.title //*5
	    row.insertCell(-1).textContent = book.year //*5
	}
    }

    /*
     * Just gets rid of the panel,
     * if we didn't do that, we'd want to update it whenever other commands change the DB
     */
    _retrieveDoneButtonCB () { //*6 Just gets rid of the panel
	this._showPanel (null)  //*6
    }

    /*
     * Pops up the panel, also sets up its selection list for user to choose
     */
    _updateButtonCB () { //*7 Pops up the panel
	this._showPanel ("updatePanel")

	// Set the panel up for selecting which book to update
	updateSelectBook.innerHTML = '<option value=""> --- </option>' //*7

	// Populate the selection list with books
	for (let book of this._bookDB.retrieveAll()) { //*7
	    let option = document.createElement("option");
	    option.text = book.title;  //*7
	    option.value = book.isbn;  //*7
	    updateSelectBook.add( option, null);  //*7
	}
    }

    /*
     * Whenever user chooses a book from selection list, 
     * we populate our window with data from that book  
    */
    _updateSelectBookCB () { //*8 Whenever user chooses from selection list
	let book = this._bookDB.retrieveBook (updateSelectBook.value)

	updateForm.isbn.value = book.isbn;  //*8
	updateForm.title.value = book.title;  //*8
	updateForm.year.value = book.year;  //*8
    }

    /*
     * Enter the change into the DB
     * NB if we didn't hide our panel at the end, we should update its selection widget
     */
    _updateSaveButtonCB () { //*9 Enter the change into the DB
	let isbn = updateSelectBook.value;

	this._bookDB.update (updateForm.isbn.value, updateForm.title.value, updateForm.year.value) //*9
	updateForm.reset();
	this._showPanel (null);
    }

    /*
     * Pops up the panel, also sets up the selection list for user to choose
     */
    _deleteButtonCB () {
	this._showPanel ("deletePanel")

	// Set the panel up for selecting which book to delete
	deleteSelectBook.innerHTML = '<option value=""> --- </option>'

	// Populate the selection list with books
	for (let book of this._bookDB.retrieveAll()) {
	    let option = document.createElement("option");
	    option.text = book.title;
	    option.value = book.isbn;
	    deleteSelectBook.add( option, null);
	}
    }

    /*
     * Actually delete the entry 
     * NB if we didn't hide our panel at the end, we should update its selection widget 
     */
    _deleteDoneButtonCB () { //*9
	let isbn = deleteSelectBook.value; //*9
	if (isbn) this._bookDB.deleteBook (isbn); //*9
	this._showPanel (null);
    }

    /*
     * For testing: create and save some test data
     */
    _generateTestDataCB () {
	this._bookDB.add (new Book ("006251587X", "Weaving the Web", 2000))
	this._bookDB.add (new Book ("0465026567", "Godel, Escher, Bach", 1999))
	this._bookDB.add (new Book ("0465030793", "I Am A Strange Loop", 2008))
	this._showPanel (null)
    }

    /*
     * For testing: clear data 
     */
    _clearDataCB () {
	if (confirm("Do you really want to delete all book data?")) {
	    this._bookDB = new BookDB()
	    this._showPanel (null) // Mainly in case retrieve display was visible
	}
    }

    /*
     * Internal routine,
     * shows one of the transient panels whose id is given (or none if arg = null)
     * and hides all the rest
     */
    _showPanel (panel) {
	for (let p of ["createPanel", "retrievePanel", "updatePanel", "deletePanel"]) {
	    document.getElementById(p).style.display = "none";
	}

	if (panel) document.getElementById(panel).style.display = "block";
    }
}

/*
 * Class that holds data about one book 
 */
class Book { //*1 Book object
    constructor (isbn, title, year) { //*1
	this._isbn = isbn;  //*1
	this._title = title; //*1
	this._year = year; //*1
    }

    update (title, year) { //*1
	if (this._title !== title) this._title = title; //*1
	let yearInt = parseInt( year);
	if (this._year !== yearInt) this._year = yearInt; //*1
    }

    // Javascript getter functions
    get isbn() {return this._isbn} //*1
    get title() {return this._title} //*1
    get year() {return this._year} //*1
}

/*
 * The database of books, an array indexed by isbn 
 */
class BookDB { //*2 Database of books
    constructor () { //*2
        this._db = {}; //*2
    }

    //  Add given book to our DB
    add (book) { //*2
	this._db[book.isbn] = book; 
    }

    //  Update an existing book entry 
    update (isbn, title, year) { //*2
	let book = this._db[isbn]; 
	book.update (title, year) 
    }

    // Retrieve one book
    retrieveBook (isbn) { //*2
	if (this._db[isbn]) {
	    return this._db[isbn]; 
	} else {
	    console.log("There is no book with ISBN " + isbn + " in the database"); 
	    return null
        }
    }

    // Return all our data, converted into an array
    retrieveAll () { //*2
	let ans = [] //*2
	for (let key of Object.keys(this._db)) { //*2
	    ans.push (this._db[key])  //*2
	}
	return ans;
    }

    //  Delete a book 
    deleteBook (isbn) { //*2
	if (this._db[isbn]) {
	    delete this._db[isbn]; 
	} else {
	    console.log("There is no book with ISBN " + isbn + " in the database"); 
        }
    }
}

/*
 * Install all our button callbacks
 */
window.onload = function () {
    new Main ();
}

</script>
</head>

<body>
  <div>
    <h1>Book Catalogue UI Demo</h1>
    <hr/>
    <button type="button" id="createButton"><span class="bigcap">C</span>reate a new book record</button>
    <button type="button" id="retrieveButton"><span class="bigcap">R</span>etrieve and list all book records</button>
    <button type="button" id="updateButton"><span class="bigcap">U</span>pdate a book record</button>
    <button type="button" id="deleteButton"><span class="bigcap">D</span>elete a book record</button>
    <hr/>
    <!-- These are for testing the system -->
    <button type="button" class="testing" id="clearButton">Clear database</button>
    <button type="button" class="testing" id="generateButton">Generate test data</button>
  <div>

  <div id="createPanel" style="display:none"> <!-- These panels stay invisible until we need them -->
    <br/><hr/><br/>
    <h2>Create a new book record</h2>

    <form id="createForm">
     <div><label>ISBN: <input name="isbn" /></label></div>
     <div><label>Title: <input name="title" /></label></div>
     <div><label>Year: <input name="year" /></label></div>
     <div><button type="button" class="inpanel" id="createSaveButton">Save</button></div>
    </form>
  </div>

  <!-- The rest of these are initialy hidden, displayed as needed -->
  <div id="retrievePanel" style="display:none">
    <br/><hr/><br/>
    <h2>Retrieve and list all book records</h2>
    <table>
     <thead><tr><th>ISBN</th><th>Title</th><th>Year</th></tr></thead>
     <tbody id="retrieveTable"></tbody>
    </table>
    <button type="button" class="inpanel" id="retrieveDoneButton">Done</button>
  </div>

  <div id="updatePanel" style="display:none">
    <br/><hr/><br/>
    <h2>Update a book record</h2>

    <form id="updateForm">
    <div>
      <label>Select book: 
      <select id="updateSelectBook"></select>
      </label>
    </div>
    <div><label>ISBN: <output name="isbn" /></label></div>
    <div><label>Title: <input name="title" /></label></div>
    <div><label>Year: <input name="year" /></label></div>
    <div><button type="button" class="inpanel" id="updateSaveButton">Save update</button></div>
  </form>
  </div>

  <div id="deletePanel" style="display:none">
    <br/><hr/><br/>
    <h2>Delete a book record</h2>

    <form id="deleteForm">
    <div>
      <label>Select book to delete: 
      <select id="deleteSelectBook"></select>
      </label>
    </div>
    <div><button type="button" class="inpanel" id="deleteDoneButton">Delete</button></div>
    </form>
  </div>

</body>
</html>
[download file]