<!--
Copyright (c) 2007-2011, Kaazing Corporation. All rights reserved.
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<link rel="stylesheet" href="css/demo.css" />
<title>Protocol Libraries Tutorial</title>
<script src="jmesnil-stomp-websocket-db0ada7/src/stomp.js"></script>
<script type="text/javascript">
function setup() {
var connectButton = new ConnectionButton(document.getElementById("connect"));
var stockTable = new StockTable(document.getElementById("stockTable"));
var urlInputField = new UrlInputField(document.getElementById("url"));
var client;
// Hanndler that is called the first time Connect button is pressed
connectButton.onconnect = function() {
var url = urlInputField.getUrl();
client = Stomp.client(url);
onconnect = function()
{
alert("Connected to message broker");
destination = "/topic/stock";
client.subscribe(destination, function(message) {
var stockData = new StockData(message.body);
stockTable.updateRow(stockData);
} );
}
var login="";
var passcode="";
client.connect(login, passcode, onconnect);
}
// Hanndler that is called the second time Connect button is pressed
connectButton.ondisconnect = function() {
client.disconnect( function() {
alert("Disconnected from stock feed");
}
);
}
}
/**
* A connection button based on the given htmlButton.
* @param {Button} htmlButton The html button to wrap.
*/
function ConnectionButton(htmlButton) {
/**
* Sets the state of this button to connected.
*/
htmlButton.connect = function() {
this._setText("Disconnect");
this._connected = true;
}
/**
* Sets the state of this button to disconnected.
*/
htmlButton.disconnect = function() {
this._setText("Connect");
this._connected = false;
}
/**
* A event fired when connect is pressed.
*/
this.onconnect;
/**
* A event fired when disconnect is pressed.
*/
this.ondisconnect;
/**
* Fires the given event if it is defined.
* @param {Function} event The event to be fired.
*/
htmlButton._fireEvent = function(event) {
if (event) {
event();
}
}
/**
* Sets the text of the button.
*/
htmlButton._setText = function(text) {
this.innerHTML = text;
}
htmlButton._connected = false;
htmlButton._connectionButton = this;
/**
* The event handler for the html button.
*/
htmlButton.onclick = function() {
if (this._connected) {
this.disconnect();
this._fireEvent(this._connectionButton.ondisconnect);
} else {
this.connect();
this._fireEvent(this._connectionButton.onconnect);
}
}
}
/**
* A history where data can be archived to and retrieved from. The history
* has a max age for the data kept in the history. Data that get older than
* the max age will be deleted from the history.
*/
function History(maxAge) {
/**
* Archived the data to the history.
* @param {Object} data The data to be archived.
*/
this.archive = function(data) {
this._dataEntries.push(data);
if (this.getHistorySize() > this.maxAge) {
this._dataEntries.shift();
}
}
/**
* Returns the data from the history at the specified age.
* @param {int} age The age of the data to be returned.
* @return {Object} The data retrieved from the history.
*/
this.retrieve = function(age) {
if (age > this.maxAge) {
throw new Error('The age should less than or equals the max age');
}
var historySize = this.getHistorySize();
var lastIndex = Math.max(0,historySize) - 1;
return this._dataEntries[lastIndex - age];
}
/**
* @return {int} the number of entries in the history at the current
* point in time.
*/
this.getHistorySize = function() {
return this._dataEntries.length;
}
/**
* The maximum age of the history.
*/
this.maxAge = maxAge;
this._dataEntries = new Array();
}
/**
* A history for multiple different stocks.
* @param {int} maxAge The maximum age the history for the diffent stocks
* will be kept.
*/
function StockHistory(maxAge) {
/**
* Returns the stock data from the history for the given ticker at the
* specified age.
* @param {String} ticker The ticker id for the stock data to retrieve
* from the history.
* @param {int} age The age of the data to be returned.
* @return {StockData} The stock data retrieved from the history.
*/
this.retrieve = function(ticker, age) {
var history = this._stockHistories[ticker];
if (history === undefined) {
return undefined;
}
return history.retrieve(age);
}
/**
* Archived the given stock data under the ticker id in the history.
* If the history is greater than the maximum age after inserting the
* data, the oldest data will be deleted from the history.
*
* @param {String} ticker The ticker id the stock data will be archived
* under.
* @param {StockData} stockData The stock data to be archived.
*/
this.archive = function(ticker, stockData) {
var history = this._stockHistories[ticker];
if (history === undefined) {
history = new History(this.maxAge);
this._stockHistories[ticker] = history;
}
history.archive(stockData);
}
/**
* Returns the change data for a given ticker between two ages in the
* history.
* @param {String} ticker The ticker id of the stocks to get the change for.
* @param {int} firstAge The age of the first stock update that should
* be used in the comparison.
* @param {int} secondAge The age of the second stock update that should
* be used in the comparison.
* @return {ChangeData} The change between the stock updates for the
* given ages.
*/
this.getChange = function(ticker, firstAge, secondAge) {
var firstStockData = this.retrieve(ticker, firstAge);
var secondStockData = this.retrieve(ticker, secondAge);
return new ChangeData(firstStockData, secondStockData);
}
/**
* The maximum age of the history.
*/
this.maxAge = maxAge;
this._stockHistories = {};
}
/**
* A class for representing the change between two stocks updates.
* @param {StockData} currentStockData The current stock data that should be
* compared against an older update.
* @param {StockData} oldStockData An older stock update to compare the current
* stock data against.
*/
function ChangeData(currentStockData, oldStockData) {
/**
* The sign of the change between the current stock price and the older
* stock price.
*
* The sign can have the following values:
* 1 : if the market is rising.
* 0 : if the market is unchanged.
* -1 : if the market is falling.
*/
this.sign = function () {
if (this.change == 0) {
return 0;
}
return this.change / Math.abs(this.change);
}
var change = 0;
var percent = 0;
if (currentStockData != undefined && oldStockData != undefined) {
change = currentStockData.price - oldStockData.price;
percent = change / oldStockData.price * 100;
}
/**
* The change between the current stock price and the older stock price.
*/
this.change = change;
/**
* The change in percent between the current stock price and the older
* stock price.
*/
this.percent = percent;
}
/**
* A stock data class constructed from the given activemq stock update
* message.
* @param {String} message The activemq message containing the information
* about the stock update.
*/
function StockData(message) {
this._COMPANY_FIELD = 0;
this._TICKER_FIELD = 1;
this._PRICE_FIELD = 2;
var fields = message.split(':');
/**
* The company name of this stock update.
*/
this.company = fields[this._COMPANY_FIELD];
/**
* The ticker id of this stock update.
*/
this.ticker = fields[this._TICKER_FIELD];
/**
* The price of this stock update.
*/
this.price = parseFloat(fields[this._PRICE_FIELD]);
}
/**
* A wrapper class for a html stock row capable of controlling the content
* of cells and their css styles.
* @param {TableRow} The html table row to use as basis for the stock row.
*/
function StockRow(htmlRow) {
/**
* Sets the company cell to the given value.
* @param {String} value The value the cell should be changed to.
*/
this.setCompany = function(value) {
this._setCellValue(this._COMPANY_CELL, value);
}
/**
* Sets the ticker cell to the given value.
* @param {String} value The value the cell should be changed to.
*/
this.setTicker = function(value) {
this._setCellValue(this._TICKER_CELL, value);
}
/**
* Sets the price cell to the given value.
* @param {int} value The value the cell should be changed to.
*/
this.setPrice = function(value) {
this._setCellValue(this._PRICE_CELL, value.toFixed(2));
}
/**
* Sets the change cell to the given value.
* @param {int} value The value the cell should be changed to.
*/
this.setChange = function(value) {
this._setCellValue(this._CHANGE_CELL, value.toFixed(2));
}
/**
* Sets the percent cell to the given value.
* @param {int} value The value the cell should be changed to.
*/
this.setPercent = function(value) {
this._setCellValue(this._PERCENT_CELL, value.toFixed(1));
}
/**
* The given sign reflects if that market is rising or falling.
* The sign can have the following values:
* 1 : if the market is rising.
* 0 : if the market is unchanged.
* -1 : if the market is falling.
* @param {int} sign The sign of the change that should be reflected in the css style.
*/
this.setSign = function(sign) {
if (sign == 0) return;
// update the table row cell styles
var changeClass = 0 < sign ? 'upChange' : 'downChange';
var percentClass = 0 < sign ? 'upPercent' : 'downPercent';
this._setCellClass(this._CHANGE_CELL, changeClass);
this._setCellClass(this._PERCENT_CELL, percentClass);
}
/**
* Update the cell value for the given index.
* @param {int} index The index of the cell to update.
* @param {String} value The value the cell should be updated to.
*/
this._setCellValue = function(index, value) {
this._htmlRow.cells[index].innerHTML = value;
}
/**
* Update the css class of the cell with the given index.
* @param {int} index The index of the cell to update.
* @param {String} value The class the updated cell should have.
*/
this._setCellClass = function(index, className) {
this._htmlRow.cells[index].className = className;
}
this._htmlRow = htmlRow;
for (var cell=0; cell < 5; cell++) {
htmlRow.insertCell(cell);
}
this._COMPANY_CELL = 0;
this._TICKER_CELL = 1;
this._PRICE_CELL = 2;
this._CHANGE_CELL = 3;
this._PERCENT_CELL = 4;
this._setCellClass(this._COMPANY_CELL, 'company');
this._setCellClass(this._TICKER_CELL, 'ticker');
this._setCellClass(this._PRICE_CELL, 'price');
this._setCellClass(this._CHANGE_CELL, 'change');
this._setCellClass(this._PERCENT_CELL, 'percent');
}
/**
* A wrapper class for the given html stock table.
* This class takes care of updating the rows in the html table.
* @param id {Table} htmlTable The html table to use for the stock table.
*/
function StockTable(htmlTable) {
/**
* Updates this stock table with the given stockData. If a row for the
* given stock data already exists that row is updated; otherwise the
* row will be added to the table.
* @param {StockData} stockData The stock data for the row to be update.
*/
this.updateRow = function(stockData) {
var stockHistory = this._stockHistory;
stockHistory.archive(stockData.ticker, stockData);
var stockRow = this._stockRows[stockData.ticker];
if (stockRow === undefined) {
stockRow = this._addStockRow(stockData.ticker);
}
var changeData = stockHistory.getChange(stockData.ticker, 0, 1);
// update the table row cell data
stockRow.setCompany(stockData.company);
stockRow.setTicker(stockData.ticker);
stockRow.setPrice(stockData.price);
stockRow.setChange(changeData.change);
stockRow.setPercent(changeData.percent);
stockRow.setSign(changeData.sign());
}
/**
* Adds a new stock row to this table under the given ticker id.
* @param {String} ticker The ticker id for the stock row.
*/
this._addStockRow = function(ticker) {
var newStockRow = new StockRow(this._addHtmlRow());
this._stockRows[ticker] = newStockRow;
return newStockRow;
}
/**
* Adds a new html row to the end of the html table this class is wrapping.
*/
this._addHtmlRow = function() {
var index = this._htmlTable.rows.length;
var htmlRow = this._htmlTable.insertRow(index);
return htmlRow;
}
this._MAX_HISTORY_AGE = 2;
this._stockHistory = new StockHistory(this._MAX_HISTORY_AGE);
this._htmlTable = htmlTable;
this._stockRows = {};
}
/**
* Fixes a background flicker problem in IE.
*/
function fixIE6BackgroundFlicker() {
if (typeof document.body.style.maxHeight == "undefined") {
try {
document.execCommand('BackgroundImageCache', false, true);
} catch(e) { /* do nothing for other browsers */ }
}
}
/**
* Creates a new url input field based on the given html input field.
* The field will be populated with a default url.
* @param {String} inputField The input field to be wrapped..
*/
function UrlInputField(htmlInputField) {
// var authority = location.host;
// if (location.search) {
// authority = location.search.slice(1) + "." + authority;
// }
// else {
// var parts = authority.split(':');
// var ports = { http:'80', https:'443' };
// authority = parts[0] + ':' + (parseInt(parts[1] || ports[location.protocol]));
// }
//location.protocol.replace("http", "ws") + "//" + authority + "/activemq";
htmlInputField.value = "";
this.getUrl = function() {
return this._htmlInputField.value;
}
this._htmlInputField = htmlInputField
}
</script>
</head>
<body onload="setup()">
<pre id="debug"></pre>
<div id="table_header">
<h1>Stock Data</h1>
<table id="stockTable" class="stockTable" cellSpacing="0" >
<thead>
<tr>
<th class="company" width="60%">Company</th>
<th class="ticker" width="100px">Ticker</th>
<th class="price" width="120px">Price</th>
<th class="change" width="120px">Change</th>
<th class="percent" width="120px">% Change</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<br>
<div id="login_div">
<label>Location</label><input id="url" style="width:240px"><br/>
</div>
<div>
<button id="connect" >Connect</button>
</div>
</div>
</body>
</html>